测试用户
2023-04-13 43393f2bb11cbf9e6af40077bbc5284660e8a754
仓库初始化
181个文件已添加
1个文件已修改
19021 ■■■■■ 已修改文件
.gitignore 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Encoded_content.json 补丁 | 查看 | 原始文档 | blame | 历史
README.md 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/modify.sql 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
encrypted_data_key.txt 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 396 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/Application.java 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/DesensitiveApi.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/DesensitiveInfo.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/JfinalModelScan.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/NoAuthorize.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/NoEncryption.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/NoLog.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/NoToken.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/RequestLimit.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/Sign.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/annotation/Table.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/CustomResponseAdvice.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/DesensitiveAspect.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/LogAspect.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/LogReturnAspect.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/RequestLimitAspect.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/ResponseHeaderAspect.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/SignatureAspect.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aspect/TokenAspect.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/aws/util/S3Util.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/component/DruidPlugin.java 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/component/RedisCache.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/ClassPathTableScanner.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/CommonConfig.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/EncryptionPropertyConfig.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/FilterConfig.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/JfinalProperties.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/Swagger2Config.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/TableScannerRegistrar.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/configure/ThreadPoolTaskConfig.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/beans/BaseResult.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/beans/Result.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/constant/Constants.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/constant/GlobalConst.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/crypto/ColumnHandler.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/crypto/CryptoHandler.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/domain/BaseModel.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/domain/DesensitiveType.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/domain/EntityModel.java 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/enums/ResultCodeEnum.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/enums/TableNameEnum.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/enums/YesNoEnum.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/exception/BizException.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/service/BaseCryptoService.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/service/CryptoService.java 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/service/JsonCryptoService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/service/SFService.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/ArrayUtils.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/BASE64DecodedMultipartFile.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/BeanHelper.java 1076 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/DateUtils.java 962 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/DesensitiveUtils.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/DistributedLock.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/DynamicBean.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/GsonUtils.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/IdUtils.java 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/JsonUtils.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/JwtTokenUtil.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/KitClassUtils.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/ModelUtils.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/PdfConverUtils.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/RegexUtils.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/RestUtil.java 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/SM4Utils.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/SecretsManagerUtils.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/SignUtil.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/SpringContextUtils.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/StringUtils.java 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/TemplateUtils.groovy 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/ThreadUtils.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/core/utils/UUID.java 484 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/filter/CorsFilter.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/filter/JwtAuthenticationTokenFilter.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/filter/RequestCachingFilter.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/redis/config/CommonRedisConfig.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/redis/serializer/KryoRedisSerializer.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/redis/util/RedisLockUtil.java 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/redis/util/RedisUtil.java 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/configure/AppDetails.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/configure/RestAuthenticationEntryPoint.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/configure/RestfulAccessDeniedHandler.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/configure/RoleBasedVoter.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/configure/SecurityConfig.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/utils/IpUtil.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/utils/SecurityHolderUtils.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/security/utils/SecurityUtils.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/servlet/JFinalStatViewServlet.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/common/sign/BufferedHttpServletRequest.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/sbg/controller/SapSBGRest.groovy 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/sbg/controller/SfSBGRest.groovy 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/CampaignUserController.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/ConfirmTxController.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/ContactController.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/FileController.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/LoanerApplicationController.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/LoanerUserController.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/MailController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/MailMergeController.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/OpportunityController.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/OrderController.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/QuotesController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/RepairController.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/SwoController.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/TSRepairController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/TestController.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/TokenController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/controller/UserFaultInfoController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/job/CleanCacheTask.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/job/GuaranteedTask.java 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/job/MailSyncTask.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/Account.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/AppConfig.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/AppPermissionConfig.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/CacheList.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/CampaignUser.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/Contact.java 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/LoanerApplication.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/LoanerUser.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/MailMerge.java 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/Opportunity.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/Order.java 402 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/Quotes.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/Repair.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/Swo.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/SysLog.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/TSRepair.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/model/UserFaultInfo.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/CampaignUserAllDto.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/CampaignUserDto.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/ContactDto.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/ContactSearchDto.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/FileRequest.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/LoanerApplicationDto.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/LoanerUserDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/MailMergeDto.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/MessageVo.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/OperatorQueryDto.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/OpportunityDto.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/OrderDto.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/QuotesDto.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/RepairDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/SFFileAddressVo.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/SFMessageVo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/SFTokenDto.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/SearchDto.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/SfCompositeRequest.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/SwoDto.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/TSRepairDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/TxConfirmDto.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/request/UserFaultInfoDto.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/CampaignUserService.java 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/ConfirmTxService.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/ContactService.java 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/FileService.java 377 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/LoanerApplicationService.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/LoanerUserService.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/MailMergeService.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/MailService.java 889 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/OpportunityService.java 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/OrderService.java 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/QuotesService.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/RepairService.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/SwoService.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/TSRepairService.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/deloitte/system/service/UserFaultInfoService.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/dev/application.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/dev/logback.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/local/application.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/local/logback.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/prod1/application.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/prod1/logback.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/prod2/application.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/prod2/logback.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sqls/theme.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/stg/application.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/stg/logback.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
New file
@@ -0,0 +1,39 @@
# Build output
target/
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
#*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# IDE
.idea/
*.iml
.settings/
.project
.classpath
.vscode/
# macOS
.DS_Store
# Azure Functions
#local.settings.json
bin/
obj/
Encoded_content.json
Binary files differ
README.md
@@ -1,4 +1,9 @@
## SSBG_PIPL
###
SSBG_PIPL_AWSCode
s3
rds
ec2
kms
rsd
redis
doc/modify.sql
New file
@@ -0,0 +1,288 @@
CREATE TABLE `app_config` (
  `id` int NOT NULL AUTO_INCREMENT,
  `appid` varchar(100) COLLATE utf8_bin DEFAULT NULL,
  `appsecret` varchar(100) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin
INSERT INTO app_config
(id, app_id, app_secret, app_name)
VALUES(1, '6LzizcRf7h8yLx28', '5033dcbfbc6509b91b7f51ecf8a4b3c13276d0297bf09e429ae3213ef9cff3280a4797ac47c8ce301aebd3a9c49fb6343a64fe7287c3e9e65ad8f8ab5ebaedef', 'SalesForce');
INSERT INTO app_config
(id, app_id, app_secret, app_name)
VALUES(2, 'xLjAH3Bc8sKVzfZG', '4517b457fefc0c59437c2ece7fe1800e08a3c09dfbce876fe5bdd5fe64e2329cfd400e7fed2c5e258e10171a41cddd2d60dbeada9745d5be4717f088d9c50b77', 'SAP');
INSERT INTO app_config
(id, app_id, app_secret, app_name)
VALUES(3, 'ovfbOZvxKqi0Hx6t', '1c24efb7c4253209101ef037ea605907301f8b332aefefbcea40c901c29d6e2680487309c3abc5121a363dafc5c59dd1ffc6d2da79715192d40483d0d3115af5', 'SPO');
INSERT INTO app_config
(id, app_id, app_secret, app_name)
VALUES(4, 'hzS6VlraGh7tjXKo', 'e7325ca5b7b673a3e0e9ceabf72090d68605cf7cda3c0392202e584cb52965fd041416842f7ce1cc2ea35c1d2d17c0b25641361c9f52854b21bdc91c6d040935', '千里马');
INSERT INTO app_config
(id, app_id, app_secret, app_name)
VALUES(5, 'axkUARv7nzMqRzkO', 'b48833bc48592bdfecad68a75b275aab80b29b7af9aa5f553f8fb503e6edb3fd2f2682907be2549c1ad0c12314db4cbd368e0b61e2fd9a29e33012108527b7cb', '小程序');
INSERT INTO app_config
(id, app_id, app_secret, app_name)
VALUES(6, '8LzKI9btg5eq3QiW', 'bb4e50bac195ae65833d1f8fdbefdb2ecbe32f483e6dfc9405d0bd4506b3e18df9d04a753495cb046418fcfe1d903341c85e4f06a4e2afe5943e9489bdc4fa60', '共通平台');
INSERT INTO app_config
(id, app_id, app_secret, app_name)
VALUES(7, 'FlcS0v228lzsn9cN', '6522e3f835acfadbf8da05d01cfc9885ccfaef6b9c7ac189aa9747f7066610e33adef181378d11265fd2d19f46c2dac7ce1c5063fbc8768f274147929806c95a', 'OBPM');
CREATE TABLE `app_permission_config` (
  `id` int NOT NULL AUTO_INCREMENT,
  `appid` varchar(45) COLLATE utf8_bin DEFAULT NULL,
  `url` varchar(45) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin
CREATE TABLE `sys_log` (
  `id` int NOT NULL AUTO_INCREMENT,
  `ret_content` tinytext COLLATE utf8_bin,
  `ip` varchar(45) COLLATE utf8_bin DEFAULT NULL,
  `is_delete` int DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `uri` varchar(45) COLLATE utf8_bin DEFAULT NULL,
  `create_by` varchar(100) COLLATE utf8_bin DEFAULT NULL,
  `update_by` varchar(100) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin
CREATE TABLE `lead` (
                        `id` bigint(20) NOT NULL,
                        `last_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                        `phone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                        `email` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                        `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                        `create_time` datetime DEFAULT NULL,
                        `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                        `update_time` datetime DEFAULT NULL,
                        `is_delete` int(3) DEFAULT NULL,
                        `sf_record_id` varchar(255) DEFAULT NULL,
                        PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `contact` (
                           `id` bigint(20) NOT NULL,
                           `last_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `phone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `email` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `create_time` datetime DEFAULT NULL,
                           `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `update_time` datetime DEFAULT NULL,
                           `is_delete` int(3) DEFAULT NULL,
                           `sf_record_id` varchar(255) DEFAULT NULL,
                           PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- pipl.consum_apply definition
CREATE TABLE `consum_apply` (
                                `id` bigint NOT NULL,
                                `phone_number` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
                                `direct_shippment_address` varchar(500) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
                                `create_by` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
                                `create_time` datetime DEFAULT NULL,
                                `update_by` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
                                `update_time` datetime DEFAULT NULL,
                                `is_delete` int DEFAULT NULL,
                                `sf_record_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE pipl.`consum_apply_equipment_set_detail` (
                                                          `id` bigint(20) NOT NULL,
                                                          `trial_user` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `create_time` datetime DEFAULT NULL,
                                                          `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `update_time` datetime DEFAULT NULL,
                                                          `is_delete` int(3) DEFAULT NULL,
                                                          `sf_record_id` varchar(255) DEFAULT NULL,
                                                          PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE pipl.`repair` (
                                                          `id` bigint(20) NOT NULL,
                                                          `address_contacts` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `address_telephone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `detailed_address` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `address_contacts_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `repair_applicant` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `address_zip_code` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `create_time` datetime DEFAULT NULL,
                                                          `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                                          `update_time` datetime DEFAULT NULL,
                                                          `is_delete` int(3) DEFAULT NULL,
                                                          `sf_record_id` varchar(255) DEFAULT NULL,
                                                          PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
alter table contact add column title varchar(255);
alter table contact add column oly_assistant_type varchar(255);
alter table contact add column job_category_picklist varchar(255);
alter table contact add column contact_address varchar(255);
alter table contact add column type varchar(255);
alter table contact add column doctor_division1 varchar(255);
alter table contact add column contact_type varchar(255);
alter table contact add column medical_staff_full_name varchar(255);
alter table contact add column mobile_phone varchar(255);
alter table contact add column unique_number varchar(255);
CREATE TABLE `aseactivity` (
                               `id` bigint(20) NOT NULL,
                               `visit_staff` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                               `reporter_ase` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                               `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                               `create_time` datetime DEFAULT NULL,
                               `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                               `update_time` datetime DEFAULT NULL,
                               `is_delete` int(3) DEFAULT NULL,
                               `sf_record_id` varchar(255) DEFAULT NULL,
                               PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `address` (
                           `id` bigint(20) NOT NULL,
                           `telephone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `zip_code` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `detailed_address` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `create_time` datetime DEFAULT NULL,
                           `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                           `update_time` datetime DEFAULT NULL,
                           `is_delete` int(3) DEFAULT NULL,
                           `sf_record_id` varchar(255) DEFAULT NULL,
                           PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `campaign_member` (
                                   `id` bigint(20) NOT NULL,
                                   `contact` bigint(20) COLLATE utf8_bin DEFAULT NULL,
                                   `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                   `create_time` datetime DEFAULT NULL,
                                   `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                   `update_time` datetime DEFAULT NULL,
                                   `is_delete` int(3) DEFAULT NULL,
                                   `sf_record_id` varchar(255) DEFAULT NULL,
                                   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
alter table campaign_member modify column contact varchar(255) COLLATE utf8_bin DEFAULT NULL;
CREATE TABLE `report`  (
  `id` bigint(20) NOT NULL,
  `voc_informer_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `caller_phone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `voc_informer_contact` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `person_in_charge_text` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `professor_sigh_text` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `staff_info_manual` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `responsible_person_HP` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `practitioner1_part` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `practitioner2_part` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `practitioner3_part` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `practitioner4_part` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `practitioner5_part` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `age` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `medical_history` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `sex` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `is_delete` int(3) DEFAULT NULL,
  `sf_record_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE pipl.`inquiry_form` (
   `id` bigint(20) NOT NULL,
   `phone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
     `email` varchar(255) COLLATE utf8_bin DEFAULT NULL,
   `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
   `create_time` datetime DEFAULT NULL,
   `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
   `update_time` datetime DEFAULT NULL,
   `is_delete` int(3) DEFAULT NULL,
   `sf_record_id` varchar(255) DEFAULT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `agency_contact` (
                                   `id` bigint(20) NOT NULL,
                                   `name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                   `doctor_division1` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                   `type` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                   `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                   `create_time` datetime DEFAULT NULL,
                                   `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                   `update_time` datetime DEFAULT NULL,
                                   `is_delete` int(3) DEFAULT NULL,
                                   `sf_record_id` varchar(255) DEFAULT NULL,
                                   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `qis_report` (
                                  `id` bigint(20) NOT NULL,
                                  `caller_phone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                  `responsible_person_h_p` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                  `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                  `create_time` datetime DEFAULT NULL,
                                  `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                  `update_time` datetime DEFAULT NULL,
                                  `is_delete` int(3) DEFAULT NULL,
                                  `sf_record_id` varchar(255) DEFAULT NULL,
                                  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `sample_order_list` (
   `id` bigint(20) NOT NULL,
   `delivery_address` varchar(255) COLLATE utf8_bin DEFAULT NULL,
     `delivery_contact` varchar(255) COLLATE utf8_bin DEFAULT NULL,
     `delivery_phone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
   `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
   `create_time` datetime DEFAULT NULL,
   `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
   `update_time` datetime DEFAULT NULL,
   `is_delete` int(3) DEFAULT NULL,
   `sf_record_id` varchar(255) DEFAULT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `tender_information` (
                                      `id` bigint(20) NOT NULL,
                                      `zhao_relation_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `zhao_relation_way` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `zhong_relation_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `zhong_relation_way` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `agent_relation_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `agent_relation_way` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `create_time` datetime DEFAULT NULL,
                                      `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
                                      `update_time` datetime DEFAULT NULL,
                                      `is_delete` int(3) DEFAULT NULL,
                                      `sf_record_id` varchar(255) DEFAULT NULL,
                                      PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
ALTER TABLE ase_activity ADD CustomerTel varchar(100) NULL;
ALTER TABLE ase_activity ADD WorkPlace varchar(100) NULL;
CREATE TABLE `campaign_user` (  `id` bigint NOT NULL,  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `create_time` datetime DEFAULT NULL,  `update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `update_time` datetime DEFAULT NULL,  `is_delete` int DEFAULT NULL,  `sf_record_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC
INSERT INTO app_permission_config(id, app_id, url)VALUES(180, 'M2os1A1gC8GH4S9v', '/ssbgapi/campaignuser/query');
INSERT INTO app_permission_config(id, app_id, url)VALUES(181, 'M2os1A1gC8GH4S9v', '/ssbgapi/campaignuser/insert');
INSERT INTO app_permission_config(id, app_id, url)VALUES(182, 'M2os1A1gC8GH4S9v', '/ssbgapi/campaignuser/update');
INSERT INTO app_permission_config(id, app_id, url)VALUES(183, 'M2os1A1gC8GH4S9v', '/ssbgapi/campaignuser/delete');
INSERT INTO app_permission_config(id, app_id, url)VALUES(184, 'M2os1A1gC8GH4S9v', '/ssbgapi/campaignuser/undelete');
INSERT INTO app_permission_config(id, app_id, url)VALUES(185, 'M2os1A1gC8GH4S9v', '/ssbgapi/campaignuser/search');
INSERT INTO app_permission_config(id, app_id, url)VALUES(186, 'M2os1A1gC8GH4S9v', '/ssbgapi/campaignuser/batchupload');
encrypted_data_key.txt
Binary files differ
pom.xml
New file
@@ -0,0 +1,396 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.deloitte.system</groupId>
    <artifactId>Arch</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>
    <properties>
        <kryo.version>4.0.2</kryo.version>
        <netty.version>4.1.77.Final</netty.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.9</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.esotericsoftware</groupId>
            <artifactId>kryo-shaded</artifactId>
            <version>${kryo.version}</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.17.0</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.70</version>
        </dependency>
        <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>
        <!-- IMPORTANT log4j升级-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
        </dependency>
        <!-- IMPORTANT log4j升级-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.6.0-alpha-2</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.14</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.21</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.1-jre</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.9</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.jfinal</groupId>
            <artifactId>activerecord</artifactId>
            <version>5.0.0</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>auth</artifactId>
            <version>2.17.191</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>aws-core</artifactId>
            <version>2.17.191</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>2.17.191</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>ses</artifactId>
            <version>2.17.191</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>secretsmanager</artifactId>
            <version>2.17.191</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <!--Swagger-UI API文档生产工具-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>com.j256.simplemagic</groupId>
            <artifactId>simplemagic</artifactId>
            <version>1.17</version>
        </dependency>
        <dependency>
            <groupId>com.opencsv</groupId>
            <artifactId>opencsv</artifactId>
            <version>5.6</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources/${env}</directory>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>dev/*</exclude>
                    <exclude>local/*</exclude>
                    <exclude>stg/*</exclude>
                    <exclude>prod1/*</exclude>
                    <exclude>prod2/*</exclude>
                </excludes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.owasp</groupId>
                <artifactId>dependency-check-maven</artifactId>
                <version>7.1.0</version>
                <configuration>
                    <autoUpdate>true</autoUpdate>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.codehaus.gmavenplus</groupId>
                <artifactId>gmavenplus-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <id>groovy-compile</id>
                        <goals>
                            <goal>addSources</goal>
                            <goal>addTestSources</goal>
                            <goal>generateStubs</goal>
                            <goal>compile</goal>
                            <goal>testGenerateStubs</goal>
                            <goal>testCompile</goal>
                            <goal>removeStubs</goal>
                            <goal>removeTestStubs</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <sources>
                        <!-- 在此节点下配置源码目录,可配置多个 -->
                        <source>
                            <directory>${project.basedir}/src/main/java</directory>
                            <includes>
                                <include>**/*.groovy</include>
                            </includes>
                        </source>
                        <source>
                            <directory>${project.basedir}/src/main/groovy</directory>
                            <includes>
                                <include>**/*.groovy</include>
                            </includes>
                        </source>
                    </sources>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <profile>
            <id>dev</id>
            <properties>
                <env>dev</env>
            </properties>
        </profile>
        <profile>
            <id>stg</id>
            <properties>
                <env>stg</env>
            </properties>
        </profile>
        <profile>
            <id>prod1</id>
            <properties>
                <env>prod1</env>
            </properties>
        </profile>
        <profile>
            <id>prod2</id>
            <properties>
                <env>prod2</env>
            </properties>
        </profile>
        <profile>
            <id>local</id>
            <properties>
                <env>local</env>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
    </profiles>
</project>
src/main/java/com/Application.java
New file
@@ -0,0 +1,239 @@
package com;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.util.JdbcConstants;
import com.alibaba.druid.wall.WallFilter;
import com.alibaba.fastjson.JSONObject;
import com.common.annotation.JfinalModelScan;
import com.common.annotation.Table;
import com.common.component.DruidPlugin;
import com.common.component.RedisCache;
import com.common.configure.JfinalProperties;
import com.common.core.utils.KitClassUtils;
import com.common.core.utils.SecretsManagerUtils;
import com.common.core.utils.SpringContextUtils;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory;
import com.jfinal.plugin.activerecord.IContainerFactory;
import com.jfinal.plugin.activerecord.cache.ICache;
import com.jfinal.plugin.activerecord.dialect.*;
import com.jfinal.template.source.StringSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
/**
 * @author 廖振钦
 * @Date 2022-01-11 10:24
 * */
@Slf4j
@Configuration
@SpringBootApplication(exclude ={DataSourceAutoConfiguration.class} )
@JfinalModelScan(basePackages = {"com.deloitte"})
@EnableAsync
@EnableScheduling // 开启定时任务功能
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        log.info("(♥◠‿◠)ノ゙  系统启动成功   ლ(´ڡ`ლ)゙  \n");
        log.info("swagger文档地址  http://"+getHost()+":"+SpringContextUtils.getEnvParam("server.port")+SpringContextUtils.getEnvParam("server.servlet.context-path")+"/doc.html");
    }
    public static String getHost(){
        try {
            InetAddress addr = InetAddress.getLocalHost();
            return addr.getHostAddress();
        } catch (UnknownHostException e) {
            log.error("启动失败",e);
        }
        return "";
    }
    private Map<DatabaseDriver, Dialect> dialectMap = new HashMap<>();
    @Autowired
    private Environment env;
    @Autowired
    private JfinalProperties jfinalProperties;
    @Autowired
    private SecretsManagerUtils secretsManagerUtils;
    @Bean
    public DruidPlugin getDruidPlugin(){
        System.setProperty("druid.mysql.usePingMethod", "false");
        JSONObject object = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.mysql"));
        String port = object.getString("port");
        String host = object.getString("host");
        String dbname = object.getString("dbname");
        String username = object.getString("username");
        String password = object.getString("password");
        String url = "jdbc:mysql://" + host + ":" + port + "/" + dbname + "?characterEncoding=utf8&useSSL=true&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&useUnicode=true&serverTimezone=Asia/Shanghai";
        DruidPlugin dbPlugin=new DruidPlugin(url , username, password);
        // 配置防火墙加强数据库安全
        WallFilter wallFilter = new WallFilter();
        wallFilter.setDbType(JdbcConstants.MYSQL);
        dbPlugin.addFilter(wallFilter);
        //配置监控
        StatFilter statFilter=new StatFilter();
        statFilter.setMergeSql(true);
        statFilter.setLogSlowSql(true);
        statFilter.setSlowSqlMillis(1500);
        // 添加 StatFilter 才会有统计数据
        dbPlugin.addFilter(statFilter);
        dbPlugin.start();
        return dbPlugin;
    }
    @Bean
    @ConditionalOnMissingBean(ICache.class)
    @ConditionalOnClass(name = "org.springframework.cache.CacheManager")
    public ICache cache() {
        return SpringContextUtils.getBean(RedisCache.class);
    }
    @Bean
    @ConditionalOnMissingBean(IContainerFactory.class)
    public IContainerFactory containerFactory() {
        // 不区分大小写 但是最后字段名统一小写
        return new CaseInsensitiveContainerFactory(true);
    }
    @Bean
    public ActiveRecordPlugin activeRecordPlugin(ICache cache,
                                                 DruidPlugin dataSource,
                                      IContainerFactory containerFactory) {
        dialectMap.put(DatabaseDriver.MYSQL, new MysqlDialect());
        dialectMap.put(DatabaseDriver.ORACLE, new OracleDialect());
        dialectMap.put(DatabaseDriver.SQLSERVER, new SqlServerDialect());
        dialectMap.put(DatabaseDriver.SQLITE, new Sqlite3Dialect());
        dialectMap.put(DatabaseDriver.POSTGRESQL, new PostgreSqlDialect());
        ActiveRecordPlugin activeRecordPlugin = new ActiveRecordPlugin(dataSource);
        activeRecordPlugin.setCache(cache);
        try {
            DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(dataSource.getDataSource().getConnection().getMetaData().getURL());
            Dialect dialect;
            if (!ObjectUtils.isEmpty(jfinalProperties.getDialect())) {
                dialect = jfinalProperties.getDialect().newInstance();
            } else {
                dialect = dialectMap.get(databaseDriver);
            }
            if (ObjectUtils.isEmpty(dialect)) {
                throw new IllegalArgumentException("dialect not be found!");
            }
            activeRecordPlugin.setDialect(dialect);
        } catch (Exception e) {
            log.error("启动失败",e);
        }
        if (!ObjectUtils.isEmpty(jfinalProperties.getShowSql())) {
            activeRecordPlugin.setShowSql(jfinalProperties.getShowSql());
        }
        if (!ObjectUtils.isEmpty(jfinalProperties.getDevMode())) {
            activeRecordPlugin.setDevMode(jfinalProperties.getDevMode());
        }
        if (!ObjectUtils.isEmpty(jfinalProperties.getTransactionLevel())) {
            activeRecordPlugin.setTransactionLevel(jfinalProperties.getTransactionLevel());
        }
        activeRecordPlugin.setContainerFactory(containerFactory);
        getSqlTemplates(activeRecordPlugin, jfinalProperties.getSqlTemplates());
        getMappingKet(activeRecordPlugin, jfinalProperties.getKitClasses());
        autoMappingTable(activeRecordPlugin);
        activeRecordPlugin.start();
        return activeRecordPlugin;
    }
    private void autoMappingTable(ActiveRecordPlugin activeRecordPlugin){
        KitClassUtils.tableclass.forEach(clazz->{
            Table table = (Table)clazz.getAnnotation(Table.class);
            activeRecordPlugin.addMapping(table.tableName(), table.primaryKey(), table.clazz());
        });
    }
    private void getSqlTemplates(ActiveRecordPlugin arp, List<String> sqlTemplates) {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<Resource> resources = new ArrayList<Resource>();
        if (!ObjectUtils.isEmpty(sqlTemplates)) {
            sqlTemplates.forEach(sqlTemplate -> {
                if (sqlTemplate != null) {
                    try {
                        Resource[] templates = resourceResolver.getResources(sqlTemplate);
                        resources.addAll(Arrays.asList(templates));
                    } catch (IOException e) {
                        log.warn("{} path not found in classpath", sqlTemplate);
                    }
                    resources.forEach(resource -> {
                        StringBuilder content = null;
                        try {
                            content = getContentByStream(resource.getInputStream());
                            arp.addSqlTemplate(new StringSource(content, true));
                        } catch (IOException e) {
                            log.error("启动失败",e);
                        }
                    });
                }
            });
        }
    }
    private void getMappingKet(ActiveRecordPlugin activeRecordPlugin, List<String> kitClasses) {
        if (!ObjectUtils.isEmpty(kitClasses)) {
            kitClasses.forEach(kitClass -> {
                try {
                    Class<?> mappingKitClass = Class.forName(kitClass);
                    Object mappingKit = mappingKitClass.newInstance();
                    Method mappingMethod = ReflectionUtils.findMethod(mappingKitClass, "mapping",
                            activeRecordPlugin.getClass());
                    ReflectionUtils.invokeMethod(mappingMethod, mappingKit, activeRecordPlugin);
                } catch (Exception e) {
                    log.warn("{} not found in classpath", kitClass);
                }
            });
        }
    }
    private StringBuilder getContentByStream(InputStream inputStream) {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String line;
            while ((line = br.readLine()) != null) {
                stringBuilder.append(line);
            }
        } catch (Exception e) {
            log.error("启动失败",e);
        }
        return stringBuilder;
    }
}
src/main/java/com/common/annotation/DesensitiveApi.java
New file
@@ -0,0 +1,17 @@
package com.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @description: 脱敏接口注解
 * @author: holden
 * @time: 2022-01-20 09:50
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DesensitiveApi {
}
src/main/java/com/common/annotation/DesensitiveInfo.java
New file
@@ -0,0 +1,29 @@
package com.common.annotation;
import com.common.core.domain.DesensitiveType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @description: 脱敏字段注解
 * @author: holden
 * @time: 2022-01-20 11:46
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DesensitiveInfo {
    DesensitiveType value() default DesensitiveType.BASIC;
    String separator() default "";
    String padStr() default "*";
    int retainLeft() default -1;
    int retainRight() default  -1;
}
src/main/java/com/common/annotation/JfinalModelScan.java
New file
@@ -0,0 +1,20 @@
package com.common.annotation;
import com.common.configure.TableScannerRegistrar;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
 * 定义Model scan
 * @author 廖振钦
 * @date 2019-08-01
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(TableScannerRegistrar.class)
public @interface JfinalModelScan {
    String[] basePackages() default {};
}
src/main/java/com/common/annotation/NoAuthorize.java
New file
@@ -0,0 +1,10 @@
package com.common.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoAuthorize {
}
src/main/java/com/common/annotation/NoEncryption.java
New file
@@ -0,0 +1,10 @@
package com.common.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoEncryption {
}
src/main/java/com/common/annotation/NoLog.java
New file
@@ -0,0 +1,13 @@
package com.common.annotation;
import java.lang.annotation.*;
/**
 * @author 廖振钦
 * @date 2022-02-18
 * */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoLog {
}
src/main/java/com/common/annotation/NoToken.java
New file
@@ -0,0 +1,13 @@
package com.common.annotation;
import java.lang.annotation.*;
/**
 * @author 廖振钦
 * @date 2022-01-17
 * */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoToken {
}
src/main/java/com/common/annotation/RequestLimit.java
New file
@@ -0,0 +1,32 @@
package com.common.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
 * one minutes request frequency is Fifty times, exceeding the wait five minutes
 * @author Administrator
 *
 */
public @interface RequestLimit {
    int DEFAULT_TIME= 60;//60秒
    int DEFAULT_COUNT= 1000;//1000次
    int DEFAULT_SIZE= 5;//5M
    /**
     * 时间范围
     * @return
     */
    int time() default DEFAULT_TIME;
    /**
     * 时间范围内次数
     * @return
     */
    int count() default DEFAULT_COUNT;
    /**
     * 请求体大小
     * @return
     */
    int size() default DEFAULT_SIZE;
}
src/main/java/com/common/annotation/Sign.java
New file
@@ -0,0 +1,14 @@
package com.common.annotation;
import java.lang.annotation.*;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sign {
}
src/main/java/com/common/annotation/Table.java
New file
@@ -0,0 +1,24 @@
package com.common.annotation;
import com.common.core.domain.BaseModel;
import com.jfinal.plugin.activerecord.Model;
import java.lang.annotation.*;
/**
 * @author 廖振钦
 * @date 2017-08-01
 * */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Table {
    String tableName();
    Class<? extends BaseModel<?>> clazz();
    String primaryKey() default "id";
}
src/main/java/com/common/aspect/CustomResponseAdvice.java
New file
@@ -0,0 +1,70 @@
package com.common.aspect;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 全局异常处理
 */
@RestControllerAdvice(basePackages = {
        "com.deloitte.system.controller"})
@Slf4j
public class CustomResponseAdvice {
    private Logger logger = LoggerFactory.getLogger(getClass());
    //定义不同的方法处理不同类型的异常
    @ExceptionHandler(Error.class)
    public Result<Object> baseErrorHandler(HttpServletRequest request, HttpServletResponse response, Error e) {
        log.error(e.getMessage(),e);
        response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
        return Result.respErr(ResultCodeEnum.RT_ERROR);
    }
    @ExceptionHandler(OutOfMemoryError.class)
    public Result<Object> baseErrorHandler(HttpServletRequest request, HttpServletResponse response, OutOfMemoryError e) {
        log.error(e.getMessage(),e);
        response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
        return Result.respErr(ResultCodeEnum.RT_ERROR);
    }
    @ExceptionHandler(Exception.class)
    public Result<Object> errorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
        log.error(e.getMessage(),e);
        response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
        return Result.respErr(ResultCodeEnum.RT_ERROR);
    }
    @ExceptionHandler({BizException.class})
    @ResponseBody
    public Result<String> handleBusinessException(HttpServletRequest request, HttpServletResponse response, BizException e){
        log.error(e.getMessage(),e);
        return Result.resp(e.getCode(), e.getMessage());
    }
    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseBody
    public Result<String> handleException(MethodArgumentNotValidException e){
        log.error(e.getMessage(),e);
        return Result.resp(ResultCodeEnum.RT_PARAM_ERROR);
    }
}
src/main/java/com/common/aspect/DesensitiveAspect.java
New file
@@ -0,0 +1,32 @@
package com.common.aspect;
import com.common.core.utils.DesensitiveUtils;
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;
/**
 * @description: 定义切面来处理敏感数据接口
 * @author: duanyashu
 * @time: 2021-03-10 14:16
 */
@Aspect
@Component
public class DesensitiveAspect {
    @Pointcut("@annotation(com.common.annotation.DesensitiveApi) || @within(com.common.annotation.DesensitiveApi)")
    public void operation(){}
    @Around("operation()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = joinPoint.proceed();
        if (obj != null && !DesensitiveUtils.isPrimitive(obj.getClass())) {
            //进行脱敏处理
            DesensitiveUtils.format(obj);
        }
        return  obj;
    }
}
src/main/java/com/common/aspect/LogAspect.java
New file
@@ -0,0 +1,113 @@
package com.common.aspect;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.common.security.utils.IpUtil;
import com.deloitte.system.model.SysLog;
import com.deloitte.system.request.FileRequest;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class LogAspect {
    @Pointcut("execution(* com..controller..*.*(..)) && (@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PatchMapping))"+
            "&& !@annotation(com.common.annotation.NoLog)")
    public void controllerMethodPointcut() {
    }
    @Before("controllerMethodPointcut()")
    public void doBefore(JoinPoint point) {
        try {
            handleLog(point);
        } catch (Throwable e) {
            log.error("LogAspect>>>>>>>>", e);
            throw e;
        }
    }
    protected void handleLog(JoinPoint point) {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String uri = request.getRequestURI();
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
        String clientType = userAgent.getOperatingSystem().getDeviceType().toString();
        log.info("clientType = " + clientType);   //客户端类型  手机、电脑、平板
        String os = userAgent.getOperatingSystem().getName();
        log.info("os = " + os);    //操作系统类型
        String ip = IpUtil.getIpAddr(request);
        log.info("ip = " + ip);    //请求ip
        String browser = userAgent.getBrowser().toString();
        log.info("browser = " + browser);    //浏览器类型
//        log.info(Thread.currentThread().getName()+",requestparam="+ JSONObject.toJSONString(point.getArgs()));
        SysLog sysLog = new SysLog();
        if(uri.endsWith("/file/upload")){
            FileRequest param=(FileRequest)point.getArgs()[0];
            JSONObject jsonObject=new JSONObject();
            jsonObject.put("fileName",param.getFileName());
            jsonObject.put("size",param.getSize());
            sysLog.ip(ip).retContent("request:" + jsonObject).uri(uri);
        }else if(uri.endsWith("/file/batchupload")){
            List<FileRequest> param=(List<FileRequest>)point.getArgs()[0];
            JSONArray jsonArray=new JSONArray();
            if(CollectionUtil.isNotEmpty(param)) {
                for (int i = 0; i < param.size(); i++) {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("fileName", param.get(i).getFileName());
                    jsonObject.put("size", param.get(i).getSize());
                    jsonArray.add(jsonObject);
                }
                sysLog.ip(ip).retContent("request:" + jsonArray).uri(uri);
            }
        }else{
            SerializeFilter filter=new ValueFilter() {
                @Override
                public Object process(Object o, String s, Object o1) {
                    if(o instanceof MultipartFile){
                        return null;
                    }
                    if(o1 instanceof MultipartFile){
                        return null;
                    }
                    return o1;
                }
            };
            sysLog.ip(ip).retContent("request:" + JSONObject.toJSONString(point.getArgs(),filter)).uri(uri);
        }
        sysLog.save();
    }
}
src/main/java/com/common/aspect/LogReturnAspect.java
New file
@@ -0,0 +1,55 @@
package com.common.aspect;
import com.alibaba.fastjson.JSONObject;
import com.common.security.utils.IpUtil;
import com.deloitte.system.model.SysLog;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
 * @author 廖振钦
 * @date 2022-02-17
 * @company deloitte
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class LogReturnAspect {
    @AfterReturning(pointcut = "execution(* com..controller..*.*(..))" +
            "&& (@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PatchMapping))"+
            "&& !@annotation(com.common.annotation.NoLog)", returning = "objectReturn")
    public void doAfterCalssReturning(Object objectReturn) throws Throwable {
        try {
            handleLog(objectReturn);
        } catch (Throwable e) {
            log.error("LogReturnAspect>>>>>>>>", e);
            throw e;
        }
    }
    protected void handleLog(Object objectReturn) {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String uri = request.getRequestURI();
        String ip = IpUtil.getIpAddr(request);
//        log.info(Thread.currentThread().getName()+",returnContent=" + JSONObject.toJSONString(objectReturn));
        SysLog sysLog = new SysLog();
        if (null != objectReturn) {
            sysLog.ip(ip).retContent("response:"+ JSONObject.toJSONString(objectReturn)).uri(uri);
            sysLog.save();
        }
    }
}
src/main/java/com/common/aspect/RequestLimitAspect.java
New file
@@ -0,0 +1,85 @@
package com.common.aspect;
import com.common.annotation.RequestLimit;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.utils.DateUtils;
import com.common.redis.util.RedisUtil;
import com.common.security.utils.IpUtil;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.security.SecureRandom;
import java.util.Set;
@Aspect
@Component
@Order(1)
@Slf4j
public class RequestLimitAspect {
    @Autowired
    private RedisUtil redisUtil;
    private static final String REQ_LIMIT = "req_limit:%s:%s:";
    private static final String REQ_LIMIT_FREQUENCY = "req_limit_frequency_%s_%s";
    /**
     * 定义拦截规则:拦截com.springboot.bcode.api包下面的所有类中,有@RequestLimit Annotation注解的方法
     * 。
     */
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController) ")
    public void pointcut() {
    }
    @Around("pointcut()")
    public Object method(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod(); // 获取被拦截的方法
        RequestLimit limt = method.getAnnotation(RequestLimit.class);
        int time,count,size;
        if (limt == null) {
            time = RequestLimit.DEFAULT_TIME;
            count = RequestLimit.DEFAULT_COUNT;
            size = RequestLimit.DEFAULT_SIZE;
        }else{
            time = limt.time();
            count = limt.count();
            size = limt.size();
        }
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = IpUtil.getIpAddr(request);
        String url = request.getRequestURI();
        // judge condition
        //大小限制
        if(request.getContentLength() > size*1024*1024){
            throw new BizException(ResultCodeEnum.LARGE_REQUEST_ERROR);
        }
        //频次限制
        String key = String.format(REQ_LIMIT, url, ip);
        Set<String> valueList = redisUtil.getKeys(key + "*");
        //将有效的过滤出来,过期时间在当前时间后的
        if (!CollectionUtils.isEmpty(valueList) && count>-1 && valueList.size() >= count) {
            throw new BizException(ResultCodeEnum.BUSY_REQUEST_ERROR);
        }
        redisUtil.set(key+ new SecureRandom().nextLong(), DateUtils.now().getTime(),time);
        return joinPoint.proceed();
    }
}
src/main/java/com/common/aspect/ResponseHeaderAspect.java
New file
@@ -0,0 +1,47 @@
package com.common.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletResponse;
/**
 * @author 谢滨璜
 * @date 2022-08-04
 * @company deloitte
 */
@Slf4j
@Aspect
@Component
@Order(-1)
public class ResponseHeaderAspect {
    @AfterReturning(pointcut = "execution(* com..controller..*.*(..))" +
            "&& (@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PatchMapping))", returning = "objectReturn")
    public void doAfterCalssReturning(Object objectReturn) throws Throwable {
        try {
            handleLog(objectReturn);
        } catch (Throwable e) {
            log.error("LogReturnAspect>>>>>>>>", e);
            throw e;
        }
    }
    protected void handleLog(Object objectReturn) {
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        response.addHeader("Content-Security-Policy","default-src 'self'");
        response.addHeader("Strict-Transport-Security","max-age=31536000; includeSubdomains");
        response.addHeader("Referrer-Policy","no-referrer-when-downgrade");
        response.addHeader("X-Permitted-Cross-Domain-Policies","all");
        response.addHeader("X-Download-Options","noopen");
    }
}
src/main/java/com/common/aspect/SignatureAspect.java
New file
@@ -0,0 +1,81 @@
package com.common.aspect;
import com.common.core.utils.SignUtil;
import com.common.sign.BufferedHttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
@Aspect
@Component
@Order(2)   //优先级应该低于Log记录切面
public class SignatureAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(StringUtils.class);
    @Around("execution(* com..controller..*.*(..)) && @annotation(com.common.annotation.Sign)")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        try {
            this.checkSign();
            return pjp.proceed();
        } catch (Throwable e) {
            LOGGER.error("SignatureAspect>>>>>>>>", e);
            throw e;
        }
    }
    private void checkSign() throws Exception {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String oldSign = request.getHeader("X-SIGN");
        if (StringUtils.isBlank(oldSign)) {
            throw new RuntimeException("取消签名Header[X-SIGN]信息");
        }
        //获取body(对应@RequestBody)
        String body = null;
        if (request instanceof BufferedHttpServletRequest) {
            body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        }
        //获取parameters(对应@RequestParam)
        Map<String, String[]> params = null;
        if (!CollectionUtils.isEmpty(request.getParameterMap())) {
            params = request.getParameterMap();
        }
        //获取path variable(对应@PathVariable)
        String[] paths = null;
        ServletWebRequest webRequest = new ServletWebRequest(request, null);
        Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(
                HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
        if (!CollectionUtils.isEmpty(uriTemplateVars)) {
            paths = uriTemplateVars.values().toArray(new String[]{});
        }
        try {
            String newSign = SignUtil.sign(body, params, paths);
            if (!newSign.equals(oldSign)) {
                throw new RuntimeException("签名不一致...");
            }
        } catch (Exception e) {
            throw new RuntimeException("验签出错...", e);
        }
    }
}
src/main/java/com/common/aspect/TokenAspect.java
New file
@@ -0,0 +1,57 @@
package com.common.aspect;
import com.common.core.utils.JwtTokenUtil;
import com.common.security.utils.SecurityHolderUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Slf4j
@Aspect
@Component
@Order(0)   //TOKEN记录切面优先级最高
public class TokenAspect {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Around("execution(* com..controller..*.*(..))" +
            "&& (@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
            "|| @annotation(org.springframework.web.bind.annotation.PatchMapping))" +
            "&& !@annotation(com.common.annotation.NoToken)")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        try {
            this.checkToken();
            return pjp.proceed();
        } catch (Throwable e) {
            log.error("LogAspect>>>>>>>>", e);
            throw e;
        }
    }
    private void checkToken(){
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String token = request.getHeader("pi-token");
        Object useridobject = jwtTokenUtil.getClaim(token).get(JwtTokenUtil.CLAIM_KEY_USERID);
        if(useridobject != null){
            String userid = useridobject.toString();
            SecurityHolderUtils.setUserId(userid);
        }
    }
}
src/main/java/com/common/aws/util/S3Util.java
New file
@@ -0,0 +1,137 @@
package com.common.aws.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import sun.misc.BASE64Encoder;
import java.util.ArrayList;
import java.util.List;
/**
 * @author Ben Pi
 * @date 2022-01-21
 */
@Slf4j
public class S3Util {
    // 创建s3对象
    private static final S3Client s3Client = S3Client.builder()
            .credentialsProvider(InstanceProfileCredentialsProvider.builder().build())
            .build();
//    public static final S3Client getS3ClientUsingEC2Instance(){
//        return S3Client.builder()
//        .credentialsProvider(InstanceProfileCredentialsProvider.builder().build())
//        .build();
//    }
    /**
     * 上传文件
     *
     * @param key 上传路径
     */
    public static String uploadFile(MultipartFile file,String key,String bucketName) {
        try {
            log.info("文件上传key:{}",key);
            // 设置文件上传对象
            PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket(bucketName).key(key).build();
            // 上传文件
            PutObjectResponse putObjectResponse = s3Client.putObject(putObjectRequest, RequestBody.fromBytes(file.getBytes()));
            if (null != putObjectResponse) {
                log.info("文件上传成功:{}",key);
                return key;
            }
            return null;
        } catch (Exception e) {
            log.error("upload file error",e);
        }
        return null;
    }
    /**
     * 文件下载
     * @param key 文件的key
     * @return
     */
    public static ResponseBytes<GetObjectResponse> downloadFile(String key,String bucketName){
        GetObjectRequest objectRequest = GetObjectRequest
                    .builder()
                    .key(key)
                    .bucket(bucketName)
                    .build();
        ResponseBytes<GetObjectResponse> objectBytes = s3Client.getObjectAsBytes(objectRequest);
        return objectBytes;
    }
    /**
     * s3文件预览
     * @param key
     */
    public static String  DownloadFromS3(String key,String bucketName) {
        BASE64Encoder encoder = new BASE64Encoder();
        GetObjectRequest objectRequest = GetObjectRequest
                .builder()
                .key(key)
                .bucket(bucketName)
                .build();
        ResponseBytes<GetObjectResponse> objectBytes = s3Client.getObjectAsBytes(objectRequest);
        byte[] data = objectBytes.asByteArray();
        // 转换成base64文件流,用于前端数据解析
        return encoder.encodeBuffer(data).trim();
    }
    public static String getURL(String keyName,String bucketName) {
        String url = null;
        try {
            GetUrlRequest request = GetUrlRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .build();
            url =s3Client.utilities().getUrl(request).toString();
            return url;
        } catch (S3Exception e) {
            e.awsErrorDetails().errorMessage();
        }
        return url;
    }
    public static DeleteObjectResponse deleteByKey(String key,String bucketName){
        DeleteObjectRequest build = DeleteObjectRequest
                .builder()
                .key(key)
                .bucket(bucketName)
                .build();
        return s3Client.deleteObject(build);
    }
    public static void deleteByKeys(List<String> keys, String bucketName){
        ArrayList<ObjectIdentifier> to_delete = new ArrayList<ObjectIdentifier>();
        for (String key : keys) {
            to_delete.add(ObjectIdentifier.builder().key(key).build());
        }
        try{
        DeleteObjectsRequest build = DeleteObjectsRequest.builder()
                .bucket(bucketName)
                .delete(Delete.builder().objects(to_delete).build())
                .build();
         s3Client.deleteObjects(build);
        }catch (S3Exception e){
            log.warn("AWS S3 删除多对象异常:"+e);
            throw e;
        }
    }
}
src/main/java/com/common/component/DruidPlugin.java
New file
@@ -0,0 +1,353 @@
package com.common.component;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.IDataSourceProvider;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.sql.DataSource;
public class DruidPlugin implements IPlugin, IDataSourceProvider {
    protected String name = null;
    protected String url;
    protected String username;
    protected String password;
    protected String publicKey;
    protected String driverClass = null;
    protected int initialSize = 1;
    protected int minIdle = 10;
    protected int maxActive = 32;
    protected long maxWait = -1L;
    protected long timeBetweenEvictionRunsMillis = 60000L;
    protected long minEvictableIdleTimeMillis = 1800000L;
    protected long timeBetweenConnectErrorMillis = 30000L;
    protected String validationQuery = "select 1";
    protected String connectionInitSql = null;
    protected String connectionProperties = null;
    protected boolean testWhileIdle = true;
    protected boolean testOnBorrow = false;
    protected boolean testOnReturn = false;
    protected boolean removeAbandoned = false;
    protected long removeAbandonedTimeoutMillis = 300000L;
    protected boolean logAbandoned = false;
    protected int maxPoolPreparedStatementPerConnectionSize = -1;
    protected Integer defaultTransactionIsolation = null;
    protected Integer validationQueryTimeout = null;
    protected Integer timeBetweenLogStatsMillis = null;
    protected Boolean keepAlive = null;
    protected String filters;
    protected List<Filter> filterList;
    protected DruidDataSource ds;
    protected volatile boolean isStarted = false;
    public DruidPlugin(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.validationQuery = autoCheckValidationQuery(url);
    }
    public DruidPlugin(String url, String username, String password, String driverClass) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.driverClass = driverClass;
        this.validationQuery = autoCheckValidationQuery(url);
    }
    public DruidPlugin(String url, String username, String password, String driverClass, String filters) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.driverClass = driverClass;
        this.filters = filters;
        this.validationQuery = autoCheckValidationQuery(url);
    }
    private static String autoCheckValidationQuery(String url) {
        if (url.startsWith("jdbc:oracle")) {
            return "select 1 from dual";
        } else if (url.startsWith("jdbc:db2")) {
            return "select 1 from sysibm.sysdummy1";
        } else if (url.startsWith("jdbc:hsqldb")) {
            return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS";
        } else {
            return url.startsWith("jdbc:derby") ? "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS" : "select 1";
        }
    }
    public void setConnectionInitSql(String sql) {
        this.connectionInitSql = sql;
    }
    public final String getName() {
        return this.name;
    }
    public final void setName(String name) {
        this.name = name;
    }
    public DruidPlugin setFilters(String filters) {
        this.filters = filters;
        return this;
    }
    public synchronized DruidPlugin addFilter(Filter filter) {
        if (this.filterList == null) {
            this.filterList = new ArrayList();
        }
        this.filterList.add(filter);
        return this;
    }
    public boolean start() {
        if (this.isStarted) {
            return true;
        } else {
            this.ds = new DruidDataSource();
            if (this.name != null) {
                this.ds.setName(this.name);
            }
            this.ds.setUrl(this.url);
            this.ds.setUsername(this.username);
            this.ds.setPassword(this.password);
            if (this.driverClass != null) {
                this.ds.setDriverClassName(this.driverClass);
            }
            this.ds.setInitialSize(this.initialSize);
            this.ds.setMinIdle(this.minIdle);
            this.ds.setMaxActive(this.maxActive);
            this.ds.setMaxWait(this.maxWait);
            this.ds.setTimeBetweenConnectErrorMillis(this.timeBetweenConnectErrorMillis);
            this.ds.setTimeBetweenEvictionRunsMillis(this.timeBetweenEvictionRunsMillis);
            this.ds.setMinEvictableIdleTimeMillis(this.minEvictableIdleTimeMillis);
            this.ds.setValidationQuery(this.validationQuery);
            if (StrKit.notBlank(this.connectionInitSql)) {
                List<String> connectionInitSqls = new ArrayList();
                connectionInitSqls.add(this.connectionInitSql);
                this.ds.setConnectionInitSqls(connectionInitSqls);
            }
            this.ds.setConnectionErrorRetryAttempts(5);
            this.ds.setTestWhileIdle(this.testWhileIdle);
            this.ds.setTestOnBorrow(this.testOnBorrow);
            this.ds.setTestOnReturn(this.testOnReturn);
            this.ds.setRemoveAbandoned(this.removeAbandoned);
            this.ds.setRemoveAbandonedTimeoutMillis(this.removeAbandonedTimeoutMillis);
            this.ds.setLogAbandoned(this.logAbandoned);
            this.ds.setMaxPoolPreparedStatementPerConnectionSize(this.maxPoolPreparedStatementPerConnectionSize);
            if (this.defaultTransactionIsolation != null) {
                this.ds.setDefaultTransactionIsolation(this.defaultTransactionIsolation);
            }
            if (this.validationQueryTimeout != null) {
                this.ds.setValidationQueryTimeout(this.validationQueryTimeout);
            }
            if (this.timeBetweenLogStatsMillis != null) {
                this.ds.setTimeBetweenLogStatsMillis((long)this.timeBetweenLogStatsMillis);
            }
            if (this.keepAlive != null) {
                this.ds.setKeepAlive(this.keepAlive);
            }
            boolean hasSetConnectionProperties = false;
            if (StrKit.notBlank(this.filters)) {
                try {
                    this.ds.setFilters(this.filters);
                    if (this.filters.contains("config")) {
                        if (StrKit.isBlank(this.publicKey)) {
                            throw new RuntimeException("Druid连接池的filter设定了config时,必须设定publicKey");
                        }
                        String decryptStr = "config.decrypt=true;config.decrypt.key=" + this.publicKey;
                        String cp = this.connectionProperties;
                        if (StrKit.isBlank(cp)) {
                            cp = decryptStr;
                        } else {
                            cp = cp + ";" + decryptStr;
                        }
                        this.ds.setConnectionProperties(cp);
                        hasSetConnectionProperties = true;
                    }
                } catch (SQLException var4) {
                    throw new RuntimeException(var4);
                }
            }
            if (!hasSetConnectionProperties && StrKit.notBlank(this.connectionProperties)) {
                this.ds.setConnectionProperties(this.connectionProperties);
            }
            this.addFilterList(this.ds);
            this.isStarted = true;
            return true;
        }
    }
    private void addFilterList(DruidDataSource ds) {
        if (this.filterList != null) {
            List<Filter> targetList = ds.getProxyFilters();
            Iterator var3 = this.filterList.iterator();
            while(var3.hasNext()) {
                Filter add = (Filter)var3.next();
                boolean found = false;
                Iterator var6 = targetList.iterator();
                while(var6.hasNext()) {
                    Filter target = (Filter)var6.next();
                    if (add.getClass().equals(target.getClass())) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    targetList.add(add);
                }
            }
        }
    }
    public boolean stop() {
        if (this.ds != null) {
            this.ds.close();
        }
        this.ds = null;
        this.isStarted = false;
        return true;
    }
    public DataSource getDataSource() {
        return this.ds;
    }
    public DruidPlugin set(int initialSize, int minIdle, int maxActive) {
        this.initialSize = initialSize;
        this.minIdle = minIdle;
        this.maxActive = maxActive;
        return this;
    }
    public DruidPlugin setDriverClass(String driverClass) {
        this.driverClass = driverClass;
        return this;
    }
    public DruidPlugin setInitialSize(int initialSize) {
        this.initialSize = initialSize;
        return this;
    }
    public DruidPlugin setMinIdle(int minIdle) {
        this.minIdle = minIdle;
        return this;
    }
    public DruidPlugin setMaxActive(int maxActive) {
        this.maxActive = maxActive;
        return this;
    }
    public DruidPlugin setMaxWait(long maxWait) {
        this.maxWait = maxWait;
        return this;
    }
    public DruidPlugin setDefaultTransactionIsolation(int defaultTransactionIsolation) {
        this.defaultTransactionIsolation = defaultTransactionIsolation;
        return this;
    }
    public DruidPlugin setValidationQueryTimeout(int validationQueryTimeout) {
        this.validationQueryTimeout = validationQueryTimeout;
        return this;
    }
    public DruidPlugin setTimeBetweenLogStatsMillis(int timeBetweenLogStatsMillis) {
        this.timeBetweenLogStatsMillis = timeBetweenLogStatsMillis;
        return this;
    }
    public DruidPlugin setKeepAlive(boolean keepAlive) {
        this.keepAlive = keepAlive;
        return this;
    }
    public DruidPlugin setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        return this;
    }
    public DruidPlugin setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
        return this;
    }
    public DruidPlugin setValidationQuery(String validationQuery) {
        this.validationQuery = validationQuery;
        return this;
    }
    public DruidPlugin setTestWhileIdle(boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
        return this;
    }
    public DruidPlugin setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
        return this;
    }
    public DruidPlugin setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
        return this;
    }
    public DruidPlugin setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
        this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
        return this;
    }
    public final DruidPlugin setTimeBetweenConnectErrorMillis(long timeBetweenConnectErrorMillis) {
        this.timeBetweenConnectErrorMillis = timeBetweenConnectErrorMillis;
        return this;
    }
    public final DruidPlugin setRemoveAbandoned(boolean removeAbandoned) {
        this.removeAbandoned = removeAbandoned;
        return this;
    }
    public final DruidPlugin setRemoveAbandonedTimeoutMillis(long removeAbandonedTimeoutMillis) {
        this.removeAbandonedTimeoutMillis = removeAbandonedTimeoutMillis;
        return this;
    }
    public final DruidPlugin setLogAbandoned(boolean logAbandoned) {
        this.logAbandoned = logAbandoned;
        return this;
    }
    public final DruidPlugin setConnectionProperties(String connectionProperties) {
        this.connectionProperties = connectionProperties;
        return this;
    }
    public final DruidPlugin setPublicKey(String publicKey) {
        this.publicKey = publicKey;
        return this;
    }
}
src/main/java/com/common/component/RedisCache.java
New file
@@ -0,0 +1,35 @@
package com.common.component;
import com.common.redis.util.RedisUtil;
import com.jfinal.plugin.activerecord.cache.ICache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RedisCache implements ICache {
    @Autowired
    private RedisUtil redisUtil;
    public  RedisCache(){}
    @Override
    public <T> T get(String s, Object o) {
        return (T) redisUtil.hashSingleGet(s, o);
    }
    @Override
    public void put(String s, Object o, Object o1) {
        redisUtil.hset(s, o, o1);
    }
    @Override
    public void remove(String s, Object o) {
        redisUtil.hDelFields(s, o);
    }
    @Override
    public void removeAll(String s) {
        redisUtil.deleteKey(s);
    }
}
src/main/java/com/common/configure/ClassPathTableScanner.java
New file
@@ -0,0 +1,68 @@
package com.common.configure;
import com.common.core.utils.KitClassUtils;
import lombok.Data;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.Nullable;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Set;
/**
 * @author 廖振钦
 */
@Data
public class ClassPathTableScanner   extends ClassPathBeanDefinitionScanner {
    private Class<? extends Annotation> annotationClass;
    public ClassPathTableScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
    public ClassPathTableScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }
    public ClassPathTableScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
        super(registry, useDefaultFilters, environment);
    }
    public ClassPathTableScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) {
        super(registry, useDefaultFilters, environment, resourceLoader);
    }
    public void registerFilters() {
        if (this.annotationClass != null) {
            addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
        }
    }
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        beanDefinitions.forEach(beanDefinition->{
            String beanclassname = beanDefinition.getBeanDefinition().getBeanClassName();
            try {
                Class onwClass = Class.forName(beanclassname);
                KitClassUtils.tableclass.add(onwClass);
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        if (beanDefinitions.isEmpty()) {
            logger.warn("没有找到 '" + Arrays.toString(basePackages) + "'包,请检查你的配置.");
        }
        return beanDefinitions;
    }
}
src/main/java/com/common/configure/CommonConfig.java
New file
@@ -0,0 +1,12 @@
package com.common.configure;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(value = "com.deloitte.system")
public class CommonConfig {
}
src/main/java/com/common/configure/EncryptionPropertyConfig.java
New file
@@ -0,0 +1,40 @@
package com.common.configure;
import com.common.core.utils.SM4Utils;
import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyResolver;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author 廖振钦
 * @date 21/12/2021 15:36
 * @desc 自动解密
 */
@Configuration
public class EncryptionPropertyConfig {
    @Bean(name = "encryptablePropertyResolver")
    public EncryptablePropertyResolver encryptablePropertyResolver() {
        return new EncryptionPropertyResolver();
    }
    class EncryptionPropertyResolver implements EncryptablePropertyResolver {
        public String resolvePropertyValue(String value) {
            if (StringUtils.isBlank(value)) {
                return value;
            }
            if (value.startsWith("DES@")) {
                return resolveDESValue(value.substring(4));
            }
            return value;
        }
        private String resolveDESValue(String value) {
            return SM4Utils.decryptStr(value);
        }
    }
}
src/main/java/com/common/configure/FilterConfig.java
New file
@@ -0,0 +1,59 @@
package com.common.configure;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.fastjson.JSONObject;
import com.common.core.utils.SecretsManagerUtils;
import com.common.filter.RequestCachingFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
@Configuration
public class FilterConfig {
//    @Bean
    public RequestCachingFilter requestCachingFilter() {
        return new RequestCachingFilter();
    }
//    @Bean
    public FilterRegistrationBean requestCachingFilterRegistration(
            RequestCachingFilter requestCachingFilter) {
        FilterRegistrationBean bean = new FilterRegistrationBean(requestCachingFilter);
        bean.setOrder(1);
        return bean;
    }
    @Bean
    public FilterRegistrationBean<com.alibaba.druid.support.http.WebStatFilter> regFilter() {
        FilterRegistrationBean<com.alibaba.druid.support.http.WebStatFilter> regFilter = new FilterRegistrationBean(new com.alibaba.druid.support.http.WebStatFilter());
        regFilter.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        regFilter.addUrlPatterns("/*");
        return regFilter ;
    }
    @Autowired
    private Environment env;
    @Autowired
    private SecretsManagerUtils secretsManagerUtils;
    @Bean
    public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        registrationBean.addInitParameter("allow", "");// IP白名单 (没有配置或者为空,则允许所有访问)
        registrationBean.addInitParameter("deny", "");// IP黑名单 (存在共同时,deny优先于allow)
        JSONObject object = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.mysql"));
        String username = object.getString("username");
        String password = object.getString("password");
        registrationBean.addInitParameter("loginUsername", username);
        registrationBean.addInitParameter("loginPassword", password);
        registrationBean.addInitParameter("resetEnable", "false");
        return registrationBean;
    }
}
src/main/java/com/common/configure/JfinalProperties.java
New file
@@ -0,0 +1 @@
/**  * $Id: JfinalProperties.java,v 1.0 2019-07-14 00:46 chenmin Exp $  */ package com.common.configure; import com.jfinal.plugin.activerecord.dialect.Dialect; import lombok.Data; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.validation.annotation.Validated; import java.util.ArrayList; import java.util.List; /**  * @author 廖振钦  * @date 2019-01-11  */ @Slf4j @Component @Data @Validated @Accessors(chain = true) @ConfigurationProperties(prefix = "jfinal") public class JfinalProperties {     /**      * SQL模板路径      */ //    private List<String> sqlTemplates = Lists.newArrayList();     private List<String> sqlTemplates = new ArrayList<>();     /**      * kit类全路径      */ //    private List<String> kitClasses = Lists.newArrayList();     private List<String> kitClasses = new ArrayList<>();     /**      * 是否打开开发模式      */     private Boolean devMode = false;     /**      * 是否显示查询SQL      */     private Boolean showSql;     /**      * 数据库方言:默认支持MYSQL,ORACLE,SQLSERVER,SQLITE,POSTGRESQL      */     private Class<? extends Dialect> dialect;     private Boolean injectDependency = false;     private String baseUploadPath;     private String baseDownloadPath;     private Integer maxPostSize;     private Integer delayInSeconds;     private String defaultBaseName;     private String defaultLocale;     private Boolean injectSuperClass;     private Boolean reportAfterInvocation;     private String datePattern;     private String urlParaSeparator;     private String viewExtension;     private Boolean clearAfterMapping;     private Boolean mappingSuperClass;     private Boolean createSession;     private String baseTemplatePath;     private Boolean sessionInView;     /**      * 是否允许覆盖Request      */     private Boolean allowRequestOverride;     /**      * 是否允许覆盖Session      */     private Boolean allowSessionOverride;     /**      * 事务级别      */     private Integer transactionLevel;     /**      * 插件加载顺序      */     private Integer configPluginOrder;     @Data     @Accessors(chain = true)     public static class TaskInfo {         private String cron;         private String task;         /**          * 是否开启守护进程          */         private Boolean daemon;         /**          * 是否开启任务,默认开启          */         private Boolean enable = true;         public TaskInfo() {         }         public TaskInfo(String cron, String task, boolean daemon, boolean enable) {             if (ObjectUtils.isEmpty(cron)) {                 throw new IllegalArgumentException("cron 不能为空.");             }             if (ObjectUtils.isEmpty(task)) {                 throw new IllegalArgumentException("task 不能为 null.");             }             this.cron = cron.trim();             this.task = task;             this.daemon = daemon;             this.enable = enable;         }     } }
src/main/java/com/common/configure/Swagger2Config.java
New file
@@ -0,0 +1,58 @@
package com.common.configure;
import org.springframework.context.annotation.*;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.SpringfoxWebMvcConfiguration;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.configuration.SwaggerCommonConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
 * @author zhliao
 * @date 2022-02-16
 * @description swagger文档配置
 * */
@Import({ SpringfoxWebMvcConfiguration.class, SwaggerCommonConfiguration.class,springfox.documentation.schema.configuration.ModelsConfiguration.class })
@ComponentScan(basePackages = {
        "springfox.documentation.swagger.readers.parameter",
        "springfox.documentation.swagger2.web",
        "springfox.documentation.swagger2.mappers"
})
@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("SBG接口")
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.deloitte.sbg"))
                .paths(PathSelectors.any())
                .build();
    }
    @Bean
    public Docket createRestApi2() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("后台系统接口")
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.deloitte.system"))
                .paths(PathSelectors.any())
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("AWS接口文档")
                .description("AWS接口文档")
                .termsOfServiceUrl("AWS接口文档")
                .version("1.0")
                .build();
    }
}
src/main/java/com/common/configure/TableScannerRegistrar.java
New file
@@ -0,0 +1,54 @@
package com.common.configure;
import com.common.annotation.JfinalModelScan;
import com.common.annotation.Table;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
@Slf4j
@Data
public class TableScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    private ResourceLoader resourceLoader;
    private Environment environment;
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(JfinalModelScan.class.getName()));
        ClassPathTableScanner scanner = new ClassPathTableScanner(registry);
        if (resourceLoader != null) {
            scanner.setResourceLoader(resourceLoader);
        }
        scanner.setAnnotationClass(Table.class);
        List<String> basePackages = new ArrayList<String>();
        for (String pkg : annoAttrs.getStringArray("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(basePackages));
    }
}
src/main/java/com/common/configure/ThreadPoolTaskConfig.java
New file
@@ -0,0 +1,49 @@
package com.common.configure;
import cn.hutool.core.thread.NamedThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
 * @auther:廖振钦
 * @date: 2022-01-04
 * */
@Configuration
public class ThreadPoolTaskConfig {
    private static final int corePoolSize = 10;               // 核心线程数(默认线程数)
    private static final int maxPoolSize = 100;                // 最大线程数
    private static final int keepAliveTime = 10;            // 允许线程空闲时间(单位:默认为秒)
    private static final int queueCapacity = 200;            // 缓冲队列数
    private static final String threadNamePrefix = "Async-Service-"; // 线程池名前缀
    @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
    public ThreadPoolTaskExecutor getAsyncExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setThreadNamePrefix(threadNamePrefix);
        // 线程池对拒绝任务的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
    @Bean
    public ThreadPoolExecutor ThreadPoolExecutor(){
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(),new NamedThreadFactory("cacha-thread-pool", true));
    }
}
src/main/java/com/common/core/beans/BaseResult.java
New file
@@ -0,0 +1,48 @@
package com.common.core.beans;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.DateUtils;
import java.io.Serializable;
/**
 * BaseResult
 *
 * @author xiaobzhou
 * date 2020-02-28 22:25
 */
public abstract class BaseResult implements Serializable {
    private String status;
    private String message;
    private Long timestamp = DateUtils.nowTimestamp();
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public Long getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
    }
    public boolean isSuccess() {
        return ResultCodeEnum.RT_SUCCESS.getCode().equals(this.getStatus());
    }
}
src/main/java/com/common/core/beans/Result.java
New file
@@ -0,0 +1,61 @@
package com.common.core.beans;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.DateUtils;
import java.util.List;
/**
 * Result
 *
 * @author xiaobzhou
 * date 2018-04-18 15:45
 */
public class Result<T> extends BaseResult {
    private String txId;
    private T object;
    private Result() {
    }
    public static <T> Result<T> resp(String code, String message) {
        Result<T> result = new Result<>();
        result.setStatus(code);
        result.setMessage(message);
        result.setTimestamp(DateUtils.nowTimestamp());
        return result;
    }
    public static <T> Result<T> resp(ResultCodeEnum resultCodeEnum) {
        Result<T> result = new Result<>();
        result.setStatus(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.name());
        result.setTimestamp(DateUtils.nowTimestamp());
        return result;
    }
    public static <T> Result<T> respErr(ResultCodeEnum resultCodeEnum) {
        Result<T> result = new Result<>();
        result.setStatus(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMsg());
        result.setTimestamp(DateUtils.nowTimestamp());
        return result;
    }
    public String getTxId() {
        return txId;
    }
    public void setTxId(String txId) {
        this.txId = txId;
    }
    public T getObject() {
        return object;
    }
    public void setObject(T object) {
        this.object = object;
    }
}
src/main/java/com/common/core/constant/Constants.java
New file
@@ -0,0 +1,63 @@
package com.common.core.constant;
/**
 * @auther:廖振钦
 * @date: 2022-01-11
 * */
public class Constants
{
    /**
     * UTF-8 字符集
     */
    public static final String UTF8 = "UTF-8";
    /**
     * GBK 字符集
     */
    public static final String GBK = "GBK";
    /**
     * http请求
     */
    public static final String HTTP = "http://";
    /**
     * https请求
     */
    public static final String HTTPS = "https://";
    /**
     * 成功标记
     */
    public static final Integer SUCCESS = 200;
    /**
     * 失败标记
     */
    public static final Integer FAIL = 500;
    /**
     * 当前记录起始索引
     */
    public static final String PAGE_NUM = "pageNum";
    /**
     * 每页显示记录数
     */
    public static final String PAGE_SIZE = "pageSize";
    /**
     * 排序列
     */
    public static final String ORDER_BY_COLUMN = "orderByColumn";
    /**
     * 排序的方向 "desc" 或者 "asc".
     */
    public static final String IS_ASC = "isAsc";
    /** 行为操作 **/
    public final static String ACTION_CREATE = "create"; //创建
    public final static String ACTION_UPDATE = "update";//更新
}
src/main/java/com/common/core/constant/GlobalConst.java
New file
@@ -0,0 +1,24 @@
package com.common.core.constant;
/**
 * @author binhuang
 */
public class GlobalConst {
    public static final Long TOKEN_EXPIRE = 3600*2L;
    public static final Long TOKEN_EXPIRE_BUFF = 3600L;
    public static final Long DATA_EXPIRE = 600L;
    public static final Long SF_TOKEN_EXPIRE = 3600L;
    /**
     * 系统收保护资源redis缓存key
     */
    public static final String TOKEN_KEY = "pipl:token:";
    public static final String TOKEN_KEY_TMP = "pipl:token:tmp:";
    public static final String SF_TOKEN_KEY = "pipl:sf:token";
    public static final String PIPL_UNCONFIRM_LOCK ="pipl:unconfirm:lock:";
    public static final String PIPL_UNCONFIRM_INSERT ="pipl:unconfirm:insert:";
    public static final String PIPL_UNCONFIRM_UPDATE ="pipl:unconfirm:update:";
    public static final String PIPL_UNCONFIRM_FILE ="pipl:unconfirm:file:";
}
src/main/java/com/common/core/crypto/ColumnHandler.java
New file
@@ -0,0 +1,41 @@
package com.common.core.crypto;
import com.common.core.utils.SM4Utils;
import lombok.extern.slf4j.Slf4j;
/**
 * @author 廖振钦
 * @Date 2022-01-11 19:02
 * */
@Slf4j
public class ColumnHandler implements CryptoHandler<String, String>{
    public String encrypto(String src){
        if(null == src){
            return null;
        }
        try{
            return SM4Utils.encryptStr(src);
        }
        catch(Exception e){
            log.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }
    public String decrypto(String src){
        if(null == src){
            return null;
        }
        try{
            return SM4Utils.decryptStr(src);
        }
        catch(Exception e){
            log.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }
}
src/main/java/com/common/core/crypto/CryptoHandler.java
New file
@@ -0,0 +1,14 @@
package com.common.core.crypto;
/**
 * @author 廖振钦
 * @Date 2022-01-11 19:24
 * */
public interface CryptoHandler<S, T> {
    // 加密
    T encrypto(S src);
    // 解密
    T decrypto(S src);
}
src/main/java/com/common/core/domain/BaseModel.java
New file
@@ -0,0 +1,125 @@
package com.common.core.domain;
import com.common.annotation.Table;
import com.common.core.utils.DateUtils;
import com.common.security.utils.SecurityUtils;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Page;
import java.util.*;
/**
 * @author 廖振钦
 * @date 20/12/2021
 */
public abstract class BaseModel<M extends BaseModel> extends Model<M> {
    public void setId(Long id) {
        set("id", id);
    }
    public Long getId() {
        return getLong("id");
    }
    public void setIsDelete(Integer isDelete) {
        set("is_delete", isDelete);
    }
    public Integer getIsDelete() {
        return getInt("is_delete");
    }
    public void setCreateBy(String createBy) {
        set("create_by", createBy);
    }
    public String getCreateBy(){
        return get("create_by");
    }
    public void setUpdateBy(String UpdateBy) {
        set("update_by", UpdateBy);
    }
    public String getUpdateBy(){
        return get("update_by");
    }
    public void setCreateTime(Date createTime) {
        set("create_time", createTime);
    }
    public Date getCreateTime() {
        return getDate("create_time");
    }
    public void setUpdateTime(Date updateTime) {
        set("update_time", updateTime);
    }
    public Date getUpdateTime() {
        return getDate("update_time");
    }
    public Page<M> pageQuery(int cpage, int pageSize, String orderField, String orderDirection,
                                               String where, int dataright) {
        String sql_select = "select *";
        StringBuffer conf = new StringBuffer(" from "+getTableName()+" where is_delete=? ");
        List<String> params = new ArrayList<String>();
        params.add("0");
        if(StrKit.notBlank(where)){
            conf.append(where);
        }
        if (StrKit.notBlank(orderField, orderDirection)) {
            conf.append(" ORDER BY ").append(" ").append(orderField).append(" ").append(orderDirection);
        }
        Page<M> page = paginate(cpage, pageSize, sql_select, conf.toString(), params.toArray());
        return page;
    }
    @Override
    public boolean save(){
        String appid = SecurityUtils.byId();
        setIsDelete(0);
        setCreateTime(DateUtils.now());
        setUpdateTime(DateUtils.now());
        setCreateBy(appid);
        setUpdateBy(appid);
        return super.save();
    }
    @Override
    public boolean update(){
        String appid = SecurityUtils.byId();
        setUpdateTime(DateUtils.now());
        setUpdateBy(appid);
        return super.update();
    }
    public String getTableName(){
        Table table = getClass().getAnnotation(Table.class);
        return table.tableName();
    }
    public List<M> findList(){
        return dao().find("select * from "+getTableName()+ " where is_delete = '0'");
    }
    public List<M> findList(String where){
        return dao().find("select * from "+getTableName()+ " where is_delete = '0' "+where);
    }
    public boolean saveOrUpdate(){
        if(null == getId()){
            return this.save();
        } else{
            return this.update();
        }
    }
}
src/main/java/com/common/core/domain/DesensitiveType.java
New file
@@ -0,0 +1,111 @@
package com.common.core.domain;
/**
 * @description: 脱敏参数
 * @author: holden
 * @time: 2022-01-20 10:50
 */
public enum DesensitiveType {
    /**
     * 中文名
     */
    CHINESE_NAME(1,0, 3,null),
    /**
     * 身份证号
     */
    ID_CARD(8,3,10,null),
    /**
     * 手机号
     */
    MOBILE_PHONE(3,2,11,null),
    /**
     * 地址
     */
    ADDRESS(6,0,10,null),
    /**
     * 电子邮件
     */
    EMAIL(0,0,5,"@"),
    /**
     * 银行卡
     */
    BANK_CARD(6,4,10,null),
    /**
     * 公司开户银行联号
     */
    CNAPS_CODE(2,0,10,null),
    /**
     * 邮政编码
     */
    ZIP_CODE(1,1,6,null),
    /**
     * 年龄
     */
    AGE(1,1,2,null),
    /**
     * 职务
     */
    TITLE_TYPE(1,1,5,null),
    /**
     * 其他文本字段类型
     */
    BASIC(1,1,10,null),
    /**
     * 日期
     */
    Date(1,1,6,null),
    /**
     * 性别
     */
    SEX(1,0, 1,null);
    private Integer retainLeft;
    private Integer retainRight;
    private Integer padSize;
    private  String separator;
    DesensitiveType(Integer retainLeft, Integer retainRight, Integer padSize, String separator) {
        this.retainLeft = retainLeft;
        this.retainRight = retainRight;
        this.padSize = padSize;
        this.separator=separator;
    }
    public void setRetainLeft(Integer retainLeft) {
        this.retainLeft = retainLeft;
    }
    public void setRetainRight(Integer retainRight) {
        this.retainRight = retainRight;
    }
    public Integer getRetainLeft() {
        return retainLeft;
    }
    public Integer getRetainRight() {
        return retainRight;
    }
    public Integer getPadSize(){
        return padSize;
    }
    public void setPadSize(Integer padSize){
        this.padSize = padSize;
    }
    public String getSeparator() {
        return separator;
    }
    public void setSeparator(String separator) {
        this.separator = separator;
    }
}
src/main/java/com/common/core/domain/EntityModel.java
New file
@@ -0,0 +1,205 @@
package com.common.core.domain;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONType;
import com.common.core.exception.BizException;
import com.common.core.utils.DateUtils;
import com.common.security.utils.SecurityUtils;
import com.jfinal.plugin.activerecord.TableMapping;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @author 廖振钦
 * @date 2022-01-29
 */
@Slf4j
@JSONType(ignores = {"tableName","id","isDelete","updateTime","createTime","createBy","updateBy"})
public abstract class EntityModel<M extends EntityModel> extends BaseModel<M> {
    @Override
    public boolean save(){
        Field[] fields = getClass().getDeclaredFields();
        for(Field f : fields){
            f.setAccessible(true);
            try {
                Object o = f.get(this);
                if(null != o) {
                    if (RecordAttrHas(humpToLine2(f.getName()))) {
                        set(humpToLine2(f.getName()), f.get(this));
                    } else if (RecordAttrHas(f.getName())) {
                        set(f.getName(), f.get(this));
                    }
                }
            } catch (IllegalAccessException e) {
            }
        }
        String appid = SecurityUtils.byId();
        setIsDelete(0);
        setCreateTime(DateUtils.now());
        setUpdateTime(DateUtils.now());
        setCreateBy(appid);
        setUpdateBy(appid);
        return super.save();
    }
    @Override
    public boolean update(){
        Field[] fields = getClass().getDeclaredFields();
        for(Field f : fields){
            f.setAccessible(true);
            if(!"id".equals(f.getName())) {
                try {
                    Object o = f.get(this);
                    if(null != o) {
                        if (RecordAttrHas(humpToLine2(f.getName()))) {
                            set(humpToLine2(f.getName()), f.get(this));
                        } else if (RecordAttrHas(f.getName())) {
                            set(f.getName(), f.get(this));
                        }
                    }
                } catch (IllegalAccessException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        String appid = SecurityUtils.byId();
        setIsDelete(0);
        setCreateTime(DateUtils.now());
        setUpdateTime(DateUtils.now());
        setCreateBy(appid);
        setUpdateBy(appid);
        return super.update();
    }
    @Override
    public boolean saveOrUpdate(){
        if(null == getId()){
            return this.save();
        } else{
            return this.update();
        }
    }
    @Override
    public M findFirst(String sql){
        M m  = super.findFirst(sql);
        if(null == m){
            return null;
        }
        RelectionField(m);
        return m;
    }
    @Override
    public M findFirst(String sql,Object...params){
        M m  = super.findFirst(sql, params);
        if(null == m){
            return null;
        }
        RelectionField(m);
        return m;
    }
    @Override
    public M findById(Object id){
        return this.findFirst("select * from "+getTableName()+" where id = ? and is_delete='0'",id);
    }
    @Override
    public List<M> findAll(){
        List<M> list = super.findAll();
        List<M> returnlist = new ArrayList<M>();
        for(M m : list){
            RelectionField(m);
            returnlist.add(m);
        }
        return returnlist;
    }
    @Override
    public List<M> find(String sql){
        List<M> list  = super.find(sql);
        List<M> returnlist = Collections.emptyList();
        for(M m : list){
            RelectionField(m);
            returnlist.add(m);
        }
        return returnlist;
    }
    @Override
    public List<M> find(String sql,Object...params){
        List<M> list  = super.find(sql, params);
        List<M> returnlist = new ArrayList<M>();
        for(M m : list){
            RelectionField(m);
            returnlist.add(m);
        }
        return returnlist;
    }
    private void RelectionField(M m){
        Field[] fields = m.getClass().getDeclaredFields();
        for(Field f : fields) {
            f.setAccessible(true);
            try {
                if(RecordAttrHas(humpToLine2(f.getName()))){
                    f.set(m, m.get(humpToLine2(f.getName())));
                }else if(RecordAttrHas(f.getName())){
                    f.set(m, m.get(f.getName()));
                }
            } catch (IllegalAccessException e) {
            }
        }
    }
    private String humpToLine2(String str) {
        Pattern humpPattern = Pattern.compile("[A-Z]");
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
    private String lineToHump(String str) {
        Pattern linePattern = Pattern.compile("_(\\w)");
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
    private Boolean RecordAttrHas(String columnName){
        Set<String> names = TableMapping.me().getTable(this.getClass()).getColumnNameSet();
        if(names.contains(columnName)){
            return true;
        }
        return false;
    }
    public String json(){
        return JSONObject.toJSONString(this);
    }
    public JSONObject jsonObject(){
        return JSONObject.parseObject(json());
    }
}
src/main/java/com/common/core/enums/ResultCodeEnum.java
New file
@@ -0,0 +1,147 @@
package com.common.core.enums;
/**
 * @DESCRIPTION:  结果代码
 * @USER: binhuang
 * @DATE: 10/12/2021
 */
public enum ResultCodeEnum {
    /**
     * 成功状态
     */
    RT_SUCCESS("0", "Success"),
    /**
     * 无效token
     */
    RT_INVALID_TOKEN("401", "Time out, Please Login again"),
    /**
     *  没有发现
     */
    RT_NOT_FOUND("404", "Invalid request url"),
    /**
     *  系统错误
     */
    RT_ERROR("500", "System exception"),
    /**
     *  系统错误
     */
    AWS_RT_ERROR("501", "AWS System exception"),
    /**
     *  访问次数频繁
     */
    BUSY_REQUEST_ERROR("502", "Busy request, please try it later!"),
    /**
     *  访问次数频繁
     */
    LARGE_REQUEST_ERROR("502", "Large request, please reduce your request body size!"),
    /**
     *  访问次数频繁
     */
    LOGIN_FAILED("503", "Login failed, please confirm your app id and secret!"),
    /**
     * 失败
     */
    RT_FAILED("100", "Failed"),
    /**
     * 参数错误
     */
    RT_PARAM_ERROR("101", "Request parameter check is invalid"),
    /**
     * 拒绝访问
     */
    RT_ACCESS_DENIED("120", "Access Denied"),
    /**
     * 空异常
     */
    RT_ACCESS_NULL("199", "Java Null Exception"),
    /**
     * 没有数据
     */
    NO_DATA("112", "No %s data"),
    /**
     * 微服务出错
     */
    RT_OUT_OF_SERVICE("130", "The server is under maintenance, please try again later"),
    /**
     * NOT NULL
     */
    PARA_NOT_NULL("140", "Parameters %s can not be null . please confirm again ."),
    /**
     * token无效
     */
    TOKEN_INVALID("101", "Invalid token, Please confirm again"),
    /**
     * token为空
     */
    TOKEN_NULL("102", "Can not get token value, Please confirm again"),
    /**
     * token过期
     */
    TOKEN_EXPIRED("103", "Token expired, Please confirm again"),
    /**
     * 没有权限
     */
    NO_PERMISSION("104", "No permission, Please confirm again"),
    /** HTTP调用异常 */
    HTTP_INVOKE_ERROR("105","Http invoke error"),
    /** HTTP调用异常 */
    QLM_TOKEN_ERROR("106","Can not get qianlima token, please contact administrator"),
    OPERATION_FAIL("115", "%s Operation failed"),
    EXECUTE_FAIL("113", "Execute fail: %s"),
    DUPLICATE_RESULT("114", "Warn,There is duplicate %s ."),
    NOT_EXIT("115", "does not exist"),
    IS_NULL("116","%s is null."),
    EXCEED_FIELD_LENGTH("117","%s length exceed %s char"),
    INTEGER_FIELD("118","%s must be an integer"),
    SORT_INVALID("119","Sort order must be desc/asc"),
    TIMEZONE_DATE_INVALID("126","Parameter %s must match pattern yyyy-MM-dd'T'HH:mm:ssXXX"),
    FIELD_CONTENT_ERROR("121","Parameter field content error"),
    SORT_NAME_INVALID("122","Sort name input error,please check"),
    LOCK_FAIL("123","Lock failed, please retry later."),
    DECRYPT_ERROR("124","Decryption error, please contact administrator."),
    FILE_SIZE_EXCEED("125","File size exceeds 30M."),
    EMAIL_ERROR("127","Send email failed, please contact administrator."),
    ;
    private String code;
    /**
     * 对应消息
     */
    private String msg;
    ResultCodeEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public String getCode() {
        return code;
    }
    public String getMsg() {
        return msg;
    }
}
src/main/java/com/common/core/enums/TableNameEnum.java
New file
@@ -0,0 +1,71 @@
package com.common.core.enums;
import java.util.ArrayList;
import java.util.List;
/**
 * @author Ali
 * Created on 10/05/2022
 */
public enum TableNameEnum {
    //ACCOUNT("account","Account"), account不涉及pi改造
    //客户人员
    CONTACT("contact", "Contact"),
    loanerApplication("loaner_application","loaner_application__c"),
    loanerUser("loaner_user","loaner_user__c"),
    mailMerge("mail_merge","Mail_Merge__c"),
    opportunity("opportunity","Opportunity"),
    ORDER("order","Order"),
    QUOTES("quotes","Quotes__c"),
    REPAIR("repair","Repair__c"),
    SWO("swo","SWO__c"),
    tsRepair("ts_repair","BusinessActivity__c"),
    userFaultInfo("user_fault_info","User_FaultInfo__c");
    //aws 表名
    private String awsTableName;
    // sf 表名
    private String sfTableName;
    public String getAwsTableName() {
        return awsTableName;
    }
    public String getSfTableName() {
        return sfTableName;
    }
    TableNameEnum(String awsTableName, String sfTableName) {
        this.awsTableName = awsTableName;
        this.sfTableName = sfTableName;
    }
    /**
     * 获取全部awsTableName值
     *
     * @return List<String>
     */
    public static List<String> getAllAwsTableName() {
        List<String> list = new ArrayList<String>();
        for (TableNameEnum item : values()) {
            list.add(item.getAwsTableName());
        }
        return list;
    }
    /**
     * 通过aws表名获取sf表名
     *
     * @param awsTableName
     * @return
     */
    public static String getSfTableNameByAwsTableName(String awsTableName) {
        for (TableNameEnum item : values()) {
            if (item.getAwsTableName().equals(awsTableName)) {
                return item.sfTableName;
            }
        }
        return null;
    }
}
src/main/java/com/common/core/enums/YesNoEnum.java
New file
@@ -0,0 +1,39 @@
package com.common.core.enums;
/**
 * @PROJECT_NAME: dtt-cdp-parent
 * @DESCRIPTION: 是否
 * @USER: shengli
 * @DATE: 27/09/2020 20:13
 */
public enum YesNoEnum {
    NO(0, "NO"),
    YES(1, "YES");
    private Integer code;
    private String remark;
    YesNoEnum(Integer code, String remark) {
        this.code = code;
        this.remark = remark;
    }
    public Integer getCode() {
        return code;
    }
    public String getRemark() {
        return remark;
    }
    public static YesNoEnum parse(Integer code) {
        if (code != null) {
            for (YesNoEnum enumA : values()) {
                if (enumA.getCode().compareTo(code) == 0) {
                    return enumA;
                }
            }
        }
        return null;
    }
}
src/main/java/com/common/core/exception/BizException.java
New file
@@ -0,0 +1,36 @@
package com.common.core.exception;
import com.common.core.enums.ResultCodeEnum;
/**
 * @author binhuang
 * @desc 自定义异常
 * @date 10/12/2021
 */
public class BizException extends RuntimeException{
    private String code;
    /**
     * @param message 异常消息
     */
    public BizException(String message) {
        this("100", message);
    }
    public BizException(ResultCodeEnum resultCodeEnum) {
        this(resultCodeEnum.getCode(),resultCodeEnum.getMsg());
    }
    /**
     * @param code    异常代码
     * @param message 异常消息
     */
    public BizException(String code, String message) {
        super(message);
        this.code = code;
    }
    public String getCode() {
        return code;
    }
}
src/main/java/com/common/core/service/BaseCryptoService.java
New file
@@ -0,0 +1,83 @@
package com.common.core.service;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
import java.util.List;
/**
 * @author 廖振钦
 * @Date 2022-01-12
 * */
public abstract class BaseCryptoService<M extends BaseModel<M>> extends CryptoService{
    protected final M dao;
    protected BaseCryptoService(M dao) {
        this.dao = dao;
    }
    public String getTableName(){
        Table table = dao.getClass().getAnnotation(Table.class);
        return table.tableName();
    }
    /**
     * 新增
     * @return    是否新增成功
     */
    public boolean save(M model) {
        model = encryptoModel(model);
        return model.save();
    }
    /**
     * 修改
     * @return    是否修改成功
     */
    public boolean update(M model) {
        model = encryptoModel(model);
        return model.update();
    }
    /**
     * 新增or修改
     * @return    是否新增or修改成功
     */
    public boolean saveOrUpdate(M model){
        if(null == model.getId()){
            return save(model);
        }else{
            return update(model);
        }
    }
    /**
     *
     * */
    public boolean LogicDeleteById(Object id){
        M m = findById(id);
        m.setIsDelete(1);
        return m.update();
    }
    /**
     *
     * */
    public M findById(Object id){
        M m = dao.findById(id);;
        return decryptoModel(m);
    }
    /**
     *
     * */
    public List<M> find(String sql, Object... objects){
        List<M> listmodel = dao.find(sql,objects);
        return decryptoModel(listmodel);
    }
}
src/main/java/com/common/core/service/CryptoService.java
New file
@@ -0,0 +1,431 @@
package com.common.core.service;
import com.common.annotation.NoEncryption;
import com.common.core.crypto.ColumnHandler;
import com.common.core.crypto.CryptoHandler;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author 廖振钦
 * @Date 2022-01-11 20:05
 * */
@Slf4j
@Service
public class CryptoService {
    // 用于数据库的某些字段加密解密
    public static final CryptoHandler<String, String> CRYPTO_HANDLER = new ColumnHandler();
    /**
     * @Title: 对record某些列解密
     */
    public List<Record> decryptoColumnRecord(List<Record> list, String[] columns){
        int size = list.size();
        for(int i = 0; i < size; i++){
            Record record = list.get(i);
            decryptoColumnRecord(record, columns);
        }
        return list;
    }
    public List<Record> decryptoColumnRecord(List<Record> list){
        int size = list.size();
        for(int i = 0; i < size; i++){
            Record record = list.get(i);
            decryptoRecord(record);
        }
        return list;
    }
    /**
     * @Title: 对record某些列解密
     */
    public Record decryptoColumnRecord(Record record, String[] columns){
        for(String column : columns){
            if(isSM(column)) {
                record.set(column, CRYPTO_HANDLER.decrypto(record.getStr(column)));
            }
        }
        return record;
    }
    public Record decryptoRecord(Record record){
        String[] columns = record.getColumnNames();
        for(String column : columns){
            if(isSM(column)) {
                record.set(column, CRYPTO_HANDLER.decrypto(record.getStr(column)));
            }
        }
        return record;
    }
    /** ========================     model加密 start  ======================== **/
    /**
     * @Title: 对model全部列加密
     */
    public <T extends Model<T>> List<T> encryptoModel(List<T> list){
        int size = list.size();
        for(int i = 0; i < size; i++){
            T model = list.get(i);
            encryptoModel(model);
        }
        return list;
    }
    /**
     * @Title: 对model某些列加密
     */
//    public <T extends Model<T>> List<T> encryptoColumnModel(List<T> list, String[] columns){
//        int size = list.size();
//        for(int i = 0; i < size; i++){
//            T model = list.get(i);
//            encryptoColumnModel(model, columns);
//        }
//        return list;
//    }
    /**
     * @Title: 对model全部列加密
     */
    public <T extends Model<T>> T encryptoModel(T model){
        String[] columnNames = model._getAttrNames();
        for(String column : columnNames){
            if(isSM(column)) {
                model.set(column, CRYPTO_HANDLER.encrypto(model.getStr(column)));
            }
        }
        return model;
    }
    /**
     * @Title: 对model某些列加密
     */
    public <T extends Model<T>> T encryptoColumnModel(T model, String[] columns){
        for(String column : columns){
            if(isSM(column)) {
                model.set(column, CRYPTO_HANDLER.encrypto(model.getStr(column)));
            }
        }
        return model;
    }
    /**
     *
     * @param model
     * @param <T>
     * @param columns
     * @return
     */
//    public <T> T encryptColumnModel(T model, String[] columns){
//        List<T> modelList = new ArrayList<>();
//        if (model instanceof Object) {
////            modelList.add(models);
//            T t = encryptColumn(model, columns);
//            return t;
//        }else if (model instanceof List) {
//            List models= (List) model;
//            modelList.addAll(models);
//            for(int i = 0; i < modelList.size(); i++){
//                T modelOne = modelList.get(i);
//                T t = encryptColumn(modelOne, columns);
//            modelList.add(t);
//            }
//            return (T) modelList;
//        }
////        int size = modelList.size();
////        List<T> ts = new ArrayList<>();
////        for(int i = 0; i < size; i++){
////            T model = modelList.get(i);
////            T t = encryptColumn(model, columns);
//////            modelList.add(t);
////        }
//
//        return null;
//    }
    /**
     * @Title: 对model某些列加密
     */
    public <T> List<T> encryptColumnModel(List<T> list, String[] columns) {
        List<T> modelList = new ArrayList<>();
        int size = list.size();
        for(int i = 0; i < size; i++){
            T model = list.get(i);
            T t = encryptColumn(model, columns);
            modelList.add(t);
        }
        return modelList;
    }
    /**
     * @Title: 对model某些列加密
     */
    public <T> T encryptColumnModel(T model, String[] columns)  {
        T t = encryptColumn(model, columns);
        return t;
    }
    /**
     *  对model字段加密
     * @param model
     * @param columns
     * @param <T>
     * @return
     */
    public <T> T encryptColumn(T model, String[] columns)  {
        Field[] fields = model.getClass().getDeclaredFields();
        for(String column : columns){
            if(isSM(column)) {
                for (int i = 0; i < fields.length; i++) {
                    if (column.equals(fields[i].getName())) {
                        String encrypto = CRYPTO_HANDLER.encrypto((String) getFieldValueByName(fields[i].getName(), model));
                        fields[i].setAccessible(true);
                        try {
                            fields[i].set(model,encrypto);
                        } catch (IllegalAccessException e) {
                            log.error("encryptColumn failed",e);
                        }
                    }
                }
            }
        }
        return model;
    }
    /**
     *  对model没有@NoEncryption字段加密
     * @param model
     * @param <T>
     * @return
     */
    public <T> T encryptModelColumns(T model) {
        Field[] fields = model.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (isSM(field.getName()) && !field.isAnnotationPresent(NoEncryption.class)) {
                Object value=getFieldValueByName(field.getName(), model);
                if(value!=null && value.getClass().isArray()){
                    String[] valueArray=(String[])value;
                    ArrayList<String> list=new ArrayList();
                    for (String s : valueArray) {
                        list.add(CRYPTO_HANDLER.encrypto(s));
                    }
                    field.setAccessible(true);
                    try {
                        field.set(model, list.toArray(new String[0]));
                    } catch (IllegalAccessException e) {
                        log.error("encryptModelColumns failed",e);
                    }
                }else {
                    String encrypto = CRYPTO_HANDLER.encrypto((String) value);
                    field.setAccessible(true);
                    try {
                        field.set(model, encrypto);
                    } catch (IllegalAccessException e) {
                        log.error("encryptModelColumns failed",e);
                    }
                }
            }
        }
        return model;
    }
    /**
     *  对model字段解密
     * @param model
     * @param columns
     * @param <T>
     * @return
     */
    public <T> T decryptColumn(T model, String[] columns) {
        try {
            Field[] fields = model.getClass().getDeclaredFields();
            for (String column : columns) {
                if (isSM(column)) {
                    for (int i = 0; i < fields.length; i++) {
                        if (column.equals(fields[i].getName())) {
                            String encrypto = CRYPTO_HANDLER.decrypto((String) getFieldValueByName(fields[i].getName(), model));
                            fields[i].setAccessible(true);
                            fields[i].set(model, encrypto);
                        }
                    }
                }
            }
            return model;
        }catch (Exception e){
            throw new BizException(ResultCodeEnum.DECRYPT_ERROR);
        }
    }
    /**
     *  对model没有@NoEncryption字段解密
     * @param model
     * @param <T>
     * @return
     */
    public <T> T decryptModelColumns(T model,Class clz) {
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            if (isSM(field.getName()) && !field.isAnnotationPresent(NoEncryption.class)) {
                String decrypto = CRYPTO_HANDLER.decrypto((String) getFieldValueByName(field.getName(), model));
                field.setAccessible(true);
                try {
                    if(decrypto!=null)
                    field.set(model, decrypto);
                } catch (IllegalAccessException e) {
                    log.error("decryptModelColumns failed",e);
                }
            }
        }
        return model;
    }
    /**
     *  对List<Model>没有@NoEncryption字段解密
     * @param models
     * @return
     */
    public <T> List<T> decryptModelListColumns(List<T> models,Class clz) {
        return models.stream().map(model -> decryptModelColumns(model,clz)).collect(Collectors.toList());
    }
    /**
     * 根据属性名获取属性值
     * @param fieldName
     * @param o
     * @return
     */
    private Object getFieldValueByName(String fieldName, Object o) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(o, new Object[]{});
            return value;
        } catch (Exception e) {
            log.error("getFieldValueByName failed",e);
        }
        return null;
    }
    /** ========================     model加密 end  ======================== **/
    /** ========================     model解密 start  ======================== **/
    /**
     * @param <T>
     * @Title: 对model全部列解密
     */
    public <T extends Model<T>> List<T> decryptoModel(List<T> list){
        int size = list.size();
        for(int i = 0; i < size; i++){
            T model = list.get(i);
            decryptoModel(model);
        }
        return list;
    }
    /**
     * @param <T>
     * @Title: 对model某些列解密
     */
    public <T extends Model<T>> List<T> decryptoColumnModel(List<T> list, String[] columns){
        int size = list.size();
        for(int i = 0; i < size; i++){
            T model = list.get(i);
            decryptoColumnModel(model, columns);
        }
        return list;
    }
    /**
     * @Title: 对model全部列解密
     */
    public <T extends Model<T>> T decryptoModel(T model){
        String[] columnNames = model._getAttrNames();
        for(String column : columnNames){
            if(isSM(column)) {
                model.set(column, CRYPTO_HANDLER.decrypto(model.getStr(column)));
            }
        }
        return model;
    }
    /**
     * @Title: 对model某些列解密
     */
    public <T extends Model<T>> T decryptoColumnModel(T model, String[] columns){
        for(String column : columns){
            if(isSM(column)) {
                model.set(column, CRYPTO_HANDLER.decrypto(model.getStr(column)));
            }
        }
        return model;
    }
    public boolean isSM(String column){
        if(!"id".equals(column) && !"create_time".equals(column) && !"update_time".equals(column) && !"is_delete".equals(column)
                && !"dataId".equals(column)&& !"isDelete".equals(column)&& !"sfRecordId".equals(column)) {
            return true;
        }else{
            return false;
        }
    }
   /** ========================     model解密 end  ======================== **/
   public Object convertor(Object src, Object target){
       Class<?> srcClass = src.getClass();
       Class<?> targetClass = target.getClass();
       Field[] fields = srcClass.getDeclaredFields();
       for (Field field : fields) {
           if (isSM(field.getName()) && !field.getName().contains("Encrypt")) {
               field.setAccessible(true);
               Object value = null;
               try {
                   value = field.get(src);
                   Class<?> methodParamTypes = getMethodParamTypes(target, "set" + captureName(field.getName())+"Encrypt");
                   Method method = targetClass.getDeclaredMethod("set" +captureName(field.getName())+"Encrypt",methodParamTypes);
                   method.setAccessible(true);
                   method.invoke(target,value);
               } catch (Exception e) {
                   log.error("convertor failed",e);
               }
           }
       }
       return target;
   }
    public String captureName(String name){
        name = name.substring(0,1).toUpperCase()+name.substring(1);
        return name;
    }
    public Class<?> getMethodParamTypes(Object classInstance,String methodName){
        Method[] methods = classInstance.getClass().getMethods();
        Class<?> param = null;
        for (int i = 0; i < methods.length; i++) {
            if (methodName.equals(methods[i].getName())) {
                param = methods[i].getParameterTypes()[0];
            }
        }
        return param;
    }
}
src/main/java/com/common/core/service/JsonCryptoService.java
New file
@@ -0,0 +1,31 @@
package com.common.core.service;
import com.alibaba.fastjson.JSONObject;
import com.common.core.crypto.ColumnHandler;
import com.common.core.crypto.CryptoHandler;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author 廖振钦
 * @Date 2022-01-20
 * */
@Service
public class JsonCryptoService {
    public static final CryptoHandler<String, String> CRYPTO_HANDLER = new ColumnHandler();
    public void encryptoJson(JSONObject object){
    }
    public void encryptoColumnJson(JSONObject object, String[] columns) {
    }
}
src/main/java/com/common/core/service/SFService.java
New file
@@ -0,0 +1,142 @@
package com.common.core.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.utils.JsonUtils;
import com.common.core.utils.RestUtil;
import com.common.core.utils.SecretsManagerUtils;
import com.common.core.utils.StringUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.request.SFTokenDto;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class SFService {
    @Value("${salesforce.tokenUrl}")
    private String tokenUrl;
    private OkHttpClient okHttpClient;
    @Autowired
    private Environment env;
    @Autowired
    private SecretsManagerUtils secretsManagerUtils;
    @Autowired
    private RedisUtil redisUtil;
    @Value("${salesforce.baseUrl}")
    private String baseUrl;
    /**
     * 获取salesforce token
     * @return
     */
    public SFTokenDto getToken() {
        SFTokenDto sfTokenDto = redisUtil.getObject(GlobalConst.SF_TOKEN_KEY,SFTokenDto.class);
        okHttpClient= new OkHttpClient.Builder()
                .connectTimeout(60*5, TimeUnit.SECONDS)
                .readTimeout(60*5, TimeUnit.SECONDS)
                .build();
        JSONObject object = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.systemauth"));
        String sfGrantType = object.getString("sf_grant_type");
        String sfClientId = object.getString("sf_client_id");
        String sfClientSecret = object.getString("sf_client_secret");
        String sfUsername = object.getString("sf_username");
        String sfPassword = object.getString("sf_password");
        // 设置请求体
        RequestBody body = new FormBody.Builder()
                .add("grant_type",sfGrantType )
                .add("client_id",sfClientId )
                .add("username", sfUsername)
                .add("password", sfPassword)
                .add("client_secret", sfClientSecret).build();
        Request.Builder reqBuilder = new Request.Builder()
                .url(tokenUrl)
                .post(body)
                .header("Accept", "application/json");
        Call call = okHttpClient.newCall(reqBuilder.build());
        Response response=null;
        String responseBody =null;
        try{
            response = call.execute();
            responseBody=response.body().string();
            log.info("salesforce token ->respData: {}", responseBody);
            if (StringUtils.isBlank(JsonUtils.string2Object(responseBody, SFTokenDto.class).getAccessToken())) {
                log.error("获取Salesforce token失败, AccessToken为空");
            }else {
                sfTokenDto = JsonUtils.string2Object(responseBody, SFTokenDto.class);
                redisUtil.set(GlobalConst.SF_TOKEN_KEY, sfTokenDto, GlobalConst.SF_TOKEN_EXPIRE);
            }
        }catch (Exception e){
            log.error("获取Salesforce token失败: ",e);
        }
        return sfTokenDto;
    }
    public JSONArray querySFData(String sql) {
        SFTokenDto tokenDto = getToken();
        String accessToken = tokenDto.getAccessToken();
        HttpHeaders header = new HttpHeaders();
        header.add("Authorization", "Bearer " + accessToken);
        StringBuilder idString = new StringBuilder();
        // 拼接URL
        StringBuilder queryUrl = idString.append(baseUrl).append("services/data/v54.0/query/?q=").append(sql);
        // 请求数据
        JSONArray jsonArray = null;
        try {
            JSONObject jsonObject = RestUtil.get(queryUrl.toString(),header);
            Object totalSize = jsonObject.get("totalSize");
            Boolean done =(Boolean) jsonObject.get("done");
            String nextRecordsUrl = jsonObject.getString("nextRecordsUrl");
            jsonArray = jsonObject.getJSONArray("records");
            if (totalSize == null) {
                log.warn("请求Salesforce数据失败,result:{}",jsonObject);
                throw new BizException("请求Salesforce数据失败");
            }
        }catch (Exception e){
            log.error("请求Salesforce数据失败",e);
            throw  e;
        }
        return jsonArray;
    }
    /**
     * 请求SF更新数据接口
     *
     * @param tableName 表名
     * @param recordId
     * @param params
     * @return 更新是否成功
     */
    public void updateSFData(String tableName,String recordId,JSONObject params) {
        SFTokenDto tokenDto = getToken();
        String accessToken = tokenDto.getAccessToken();
        HttpHeaders header = new HttpHeaders();
        header.add("Authorization", "Bearer " + accessToken);
        header.add("Content-Type","application/json");
        StringBuilder idString = new StringBuilder();
        // 拼接URL
        StringBuilder url = idString.append(baseUrl).append("services/data/v54.0/sobjects/").append(tableName).append("/").append(recordId);
        log.info("updateSFData 请求地址:{}",url.toString());
        // 请求数据
        ResponseEntity sfRes = RestUtil.patchNative(url.toString(), header, null, params.toJSONString());
        log.info("更新Salesforce数据,result:{}",sfRes.toString());
        int statusCodeValue = sfRes.getStatusCodeValue();
        //请求成功 http status:204
        if(statusCodeValue!=204){
            log.error("更新Salesforce数据失败,result:{}",sfRes.toString());
            throw new BizException("更新Salesforce数据失败");
        }
    }
}
src/main/java/com/common/core/utils/ArrayUtils.java
New file
@@ -0,0 +1,9 @@
package com.common.core.utils;
/**
 * @author xiaobzhou
 * date 2020-01-21 15:33
 */
public class ArrayUtils extends org.apache.commons.lang3.ArrayUtils {
}
src/main/java/com/common/core/utils/BASE64DecodedMultipartFile.java
New file
@@ -0,0 +1,86 @@
package com.common.core.utils;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import java.io.*;
/**
 * @Author holfeng
 * @Date 15:21 10/02/2022
 * @Version 1.0
 **/
public class BASE64DecodedMultipartFile implements MultipartFile {
    private byte[] fileContent;
    private String header;
    public BASE64DecodedMultipartFile(byte[] fileContent, String header) {
        this.fileContent = fileContent;
        this.header = header.split(";")[0];
    }
    @Override
    public String getName() {
        return header.split("/")[1];
    }
    @Override
    public String getOriginalFilename() {
        return header.split("/")[1];
    }
    @Override
    public String getContentType() {
        return header.split(":")[1];
    }
    @Override
    public boolean isEmpty() {
        return fileContent == null || fileContent.length == 0;
    }
    @Override
    public long getSize() {
        return fileContent.length;
    }
    @Override
    public byte[] getBytes() throws IOException {
        return fileContent;
    }
    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(fileContent);
    }
    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        FileOutputStream stream=null;
        try {
            stream = new FileOutputStream(dest);
            stream.write(fileContent);
        }catch (Exception e){
            throw e;
        }finally {
            if(stream!=null)stream.close();
        }
    }
    public static MultipartFile base64ToMultipart(String base64) throws IOException {
        String[] baseStrs = base64.split(",");
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] b = new byte[0];
        b = decoder.decodeBuffer(baseStrs[1]);
        for (int i = 0; i < b.length; ++i) {
            if (b[i] < 0) {
                b[i] += 256;
            }
        }
        return new BASE64DecodedMultipartFile(b, baseStrs[0]);
    }
}
src/main/java/com/common/core/utils/BeanHelper.java
New file
@@ -0,0 +1,1076 @@
package com.common.core.utils;
import org.hibernate.Hibernate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import java.beans.PropertyDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
/**
 * Bean工具类.
 *
 * @author LuoGang
 * @date 2019-04-13 16:53
 */
public class BeanHelper {
    /**
     * 分隔符号
     */
    public static final char UNDERLINE = '_';
    /**
     * 符号.
     */
    public static final String POINT = ".";
    /**
     * 系统的换行符
     */
    public static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
    /**
     * 根据class缓存其所有的PropertyDescriptor
     */
    private static Map<Class<?>, PropertyDescriptor[]> propertyDescriptorsCache = new HashMap<>();
    /**
     * 得到cls定义的所有字段,不包含class
     *
     * @param cls Class
     * @return cls下定义的字段
     */
    public static PropertyDescriptor[] getPropertyDescriptors(Class<?> cls) {
        PropertyDescriptor[] cache = propertyDescriptorsCache.get(cls);
        if (cache == null) {
            List<PropertyDescriptor> pds = new ArrayList<>();
            PropertyDescriptor[] arr = BeanUtils.getPropertyDescriptors(cls);
            for (PropertyDescriptor pd : arr) {
                if ("class".equals(pd.getName())) {
                    // 不需要class这个属性
                    continue;
                }
                pds.add(pd);
            }
            cache = pds.toArray(new PropertyDescriptor[pds.size()]);
            propertyDescriptorsCache.put(cls, cache);
        }
        return cache;
    }
    /**
     * 得到obj对象定义的所有字段。注意obj可能是Hibernate的托管对象,要处理
     *
     * @param obj
     * @return obj定义的字段
     * @see {@link Hibernate#getClass(Object)}
     */
    public static PropertyDescriptor[] getPropertyDescriptors(Object obj) {
        Class<?> cls = Hibernate.getClass(obj);
        return getPropertyDescriptors(cls);
    }
    /**
     * 得到属性的类型,返回class name.
     *
     * @param entity       实体类
     * @param propertyName 字段名
     * @return
     */
    public static final String getPropertyType(Object entity, String propertyName) {
        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(Hibernate.getClass(entity), propertyName);
        if (null != pd) {
            return pd.getPropertyType().getName();
        } else {
            return null;
        }
    }
    /**
     * 得到属性的PropertyDescriptor
     *
     * @param entity       实体对象
     * @param propertyName 字段名
     * @return 字段的PropertyDescriptor对象
     */
    public static final PropertyDescriptor getPropertyDescriptor(Object entity, String propertyName) {
        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(Hibernate.getClass(entity), propertyName);
        return pd;
    }
    /**
     * 为对象entity设置value.
     *
     * @param entity       实体类
     * @param propertyName 字段名
     * @param value        值
     */
    public static final void setPropertyValue(Object entity, String propertyName, Object value) {
        if (propertyName.indexOf(POINT) > -1) {
            String[] propertyNames = StringUtils.split(propertyName, '.');
            for (int i = 0, l = propertyNames.length - 1; i < l; i++) {
                entity = getPropertyValue(entity, propertyNames[i]);
                if (entity == null) {
                    return;
                }
            }
            propertyName = propertyNames[propertyNames.length - 1];
        }
        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(Hibernate.getClass(entity), propertyName);
        if (pd != null) {
            setPropertyValue(entity, pd, value);
        }
    }
    public static final void setPropertyValue(Object entity, String propertyName, Object value,Class clazz) {
        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, propertyName);
        if (pd != null) {
            setPropertyValue(entity, pd, value);
        }
    }
    /**
     * 为对象entity设置value.
     *
     * @param entity 实体类
     * @param pd     要读取的属性PropertyDescriptord对象
     * @param value  值
     */
    public static final void setPropertyValue(@NotNull Object entity, @NotNull PropertyDescriptor pd, Object value) {
        Method writeMethod = pd.getWriteMethod();
        if (writeMethod != null) {
            try {
                value = BeanHelper.convert(value, pd.getPropertyType());
                if (value == null || pd.getPropertyType().isAssignableFrom(value.getClass())) { // 必须是同样类型的才匹配,否则不处理
                    ReflectionUtils.invokeMethod(writeMethod, entity, value);
                }
            } catch (Exception e) {
                throw new RuntimeException(entity.getClass() + POINT + pd.getName() + "写入值:" + value + "出现异常\n" + e.getMessage(), e);
            }
        }
    }
    /**
     * 得到entity中propertyName属性的值.
     *
     * @param entity       实体对象
     * @param propertyName 要读取的属性名
     * @return 得到的值
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static final <T> T getPropertyValue(Object entity, String propertyName) {
        if (entity == null) {
            return null;
        }
        if (entity instanceof Map) {
            return (T) ((Map) entity).get(propertyName);
        }
        if (propertyName.indexOf(POINT) > 0) {
            String[] propertyNames = StringUtils.split(propertyName, '.');
            for (String name : propertyNames) {
                entity = getPropertyValue(entity, name);
            }
            return (T) entity;
        }
        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(Hibernate.getClass(entity), propertyName);
        if (pd == null) {
            Field field = ReflectionUtils.findField(Hibernate.getClass(entity), propertyName);
            if (field != null) {
                Method setAccessible = ReflectionUtils.findMethod(field.getClass(), "setAccessible", Boolean.TYPE);
                ReflectionUtils.invokeMethod(setAccessible, field, true);
                return (T) ReflectionUtils.getField(field, entity);
            }
        }
        return getPropertyValue(entity, pd);
    }
    /**
     * 得到entity中PropertyDescriptor属性的值.
     *
     * @param entity 实体对象
     * @param pd     要读取的属性PropertyDescriptord对象
     * @return 得到的值
     */
    @SuppressWarnings("unchecked")
    public static final <T> T getPropertyValue(Object entity, PropertyDescriptor pd) {
        if (entity == null || pd == null || pd.getReadMethod() == null) {
            return null;
        }
        Method readMethod = pd.getReadMethod();
        try {
            // if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
            // readMethod.setAccessible(true);
            // }
            // return (T) readMethod.invoke(entity);
            return (T) ReflectionUtils.invokeMethod(readMethod, entity);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 得到组或Collection中下标index的值.
     *
     * @param array 数组或Collection
     * @param index 下标,0开始
     * @return 得到的值
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static final <T> T getValue(Object array, int index) {
        if (array == null || index < 0) {
            return null;
        }
        if (array instanceof Collection) { // 集合
            if (((Collection) array).size() <= index) { // 下标越界
                return null;
            }
            if (array instanceof List) {
                return (T) (((List) array).get(index));
            }
            Iterator it = ((Collection) array).iterator();
            while (index-- > 0) {
                it.next();
            }
            return (T) it.next();
        }
        if (array.getClass().isArray()) { // 数组
            if (Array.getLength(array) <= index) { // 下标越界
                return null;
            }
            return (T) Array.get(array, index);
        }
        return null;
    }
    /**
     * 新建clazz对象,并复制source中的属性
     *
     * @param source
     * @param clazz
     * @return
     */
    public static <T> T copyAs(Object source, Class<T> clazz) {
        T target = BeanUtils.instantiateClass(clazz);
        copy(source, target);
        return target;
    }
    /**
     * 复制一个链表,目标链表节点类型是指定的Class
     *
     * @param list  源数据
     * @param clazz 目标链表节点类型
     * @return
     */
    public static <T> List<T> copyAs(Collection<?> list, Class<T> clazz) {
        return list.stream().map(source -> copyAs(source, clazz)).collect(Collectors.toList());
    }
    /**
     * 对给定的source对象初始化所有保留类型的字段默认值。其中数字默认为0,boolean默认为false.
     *
     * @param source           要处理的对象
     * @param ignoreProperties 不需要处理的字段
     */
    public static final <T> void initPrimitiveFields(T source, String... ignoreProperties) {
        Class<?> cls = Hibernate.getClass(source);
        PropertyDescriptor[] targetPds = getPropertyDescriptors(cls);
        List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : Collections.emptyList();
        Class<?> propertyClass;
        for (PropertyDescriptor pd : targetPds) {
            if (ignoreList.contains(pd.getName()) || pd.getWriteMethod() == null || getPropertyValue(source, pd) != null) {
                continue;
            }
            propertyClass = pd.getPropertyType();
            if (Boolean.class.equals(propertyClass) || Boolean.TYPE.equals(propertyClass)) {
                // boolean类型默认为false
                setPropertyValue(source, pd, false);
            } else if (Number.class.isAssignableFrom(propertyClass)) {
                // 数字类型默认为0
                setPropertyValue(source, pd, 0);
            }
        }
    }
    /**
     * 直接clone一个对象
     *
     * @param source           要复制的对象
     * @param ignoreProperties 忽略的字段
     * @return 复制的对象
     */
    @SuppressWarnings("unchecked")
    public static final <T> T copy(T source, String... ignoreProperties) {
        Class<T> cls = Hibernate.getClass(source);
        T obj = BeanUtils.instantiateClass(cls);
        return BeanHelper.copy(source, obj, ignoreProperties);
    }
    /**
     * 将source中所有不为null的值copy到target中.
     * 与{@link BeanUtils#copyProperties(Object, Object, String...)}不同之处在于本方法不copy为null的属性
     *
     * @param source           要复制的源对象
     * @param target           目标对象
     * @param ignoreProperties 需要忽略的字段
     * @return 返回target
     * @see BeanUtils#copyProperties(Object, Object, String...)
     */
    public static final <T> T copy(Object source, T target, String... ignoreProperties) {
        return BeanHelper.copy(source, target, false, ignoreProperties);
    }
    /**
     * 将source中所有不为null的Encrypt值copy到target中.(eg:copy soure.nameEncrypt to target.name)
     * 与{@link BeanUtils#copyProperties(Object, Object, String...)}不同之处在于本方法不copy为null的属性
     *
     * @param source           要复制的源对象
     * @param target           目标对象
     * @param ignoreProperties 需要忽略的字段
     * @return 返回target
     * @see BeanUtils#copyProperties(Object, Object, String...)
     */
    public static final <T> T copyEncrypt(Object source, T target, String... ignoreProperties) {
        return BeanHelper.copyEncrypt(source, target, false,"Encrypt", ignoreProperties);
    }
    /**
     * 将source中所有不为null的值copy到target中.
     * 仅复制基础类型的数据,只对JAVA保留类型/数字/String/Date以及其数组的数据进行复制
     * 与{@link BeanUtils#copyProperties(Object, Object, String...)}不同之处在于本方法不copy为null的属性
     *
     * @param source           要复制的源对象
     * @param target           目标对象
     * @param ignoreProperties 需要忽略的字段
     * @return 返回target
     * @see BeanUtils#copyProperties(Object, Object, String...)
     */
    public static final <T> T copyBasic(Object source, T target, String... ignoreProperties) {
        return BeanHelper.copy(source, target, true, ignoreProperties);
    }
    /**
     * 判断指定的clazz是否为基础数据类型
     *
     * @param clazz
     * @return
     */
    public static boolean isBasicClass(Class<?> clazz) {
        if (clazz.isArray()) {
            clazz = clazz.getComponentType();
        }
        if ((clazz.isPrimitive()// JAVA的保留类型,boolean, byte, char, short, int, long, float, double.
                || Number.class.isAssignableFrom(clazz) // 数字
                || Boolean.class.isAssignableFrom(clazz) // Boolean
                || Character.class.isAssignableFrom(clazz) // Character
                || CharSequence.class.isAssignableFrom(clazz) // 字符串
                || Date.class.isAssignableFrom(clazz))// 日期
        ) {
            return true;
        }
        return false;
    }
    /**
     * 将source中所有不为null的值copy到target中.
     * 与{@link BeanUtils#copyProperties(Object, Object, String...)}不同之处在于本方法不copy为null的属性
     *
     * @param source           要复制的源对象
     * @param target           目标对象
     * @param basicOnly        是否仅复制基础类型的数据,如果为true,则只对JAVA保留类型/数字/String/Date以及其数组的数据进行复制
     * @param ignoreProperties 需要忽略的字段
     * @return 返回target
     * @see BeanUtils#copyProperties(Object, Object, String...)
     */
    public static final <T> T copy(Object source, T target, boolean basicOnly, String... ignoreProperties) {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> cls = Hibernate.getClass(source);
        Class<?> actualEditable = Hibernate.getClass(target);
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
        for (PropertyDescriptor targetPd : targetPds) {
            if (basicOnly) {
                Class<?> propertyType = targetPd.getPropertyType();
                if (propertyType.isArray()) {
                    propertyType = propertyType.getComponentType();
                }
                if (!isBasicClass(propertyType)) {
                    // 复杂对象不处理
                    continue;
                }
            }
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || (!ignoreList.contains(targetPd.getName())))) {
                PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(cls, targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null) {// && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            // if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            // readMethod.setAccessible(true);
                            // }
                            Object value = ReflectionUtils.invokeMethod(readMethod, source);
                            if (value == null) {
                                // 为null的值不处理
                                continue;
                            }
                            setPropertyValue(target, targetPd, value);
                        } catch (Throwable ex) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }
        return target;
    }
    /**
     * 将source中所有不为null的值copy到target中.
     * 与{@link BeanUtils#copyProperties(Object, Object, String...)}不同之处在于本方法不copy为null的属性
     *
     * @param source           要复制的源对象
     * @param target           目标对象
     * @param basicOnly        是否仅复制基础类型的数据,如果为true,则只对JAVA保留类型/数字/String/Date以及其数组的数据进行复制
     * @param suffix           要复制的字段添加后缀
     * @param ignoreProperties 需要忽略的字段
     * @return 返回target
     * @see BeanUtils#copyProperties(Object, Object, String...)
     */
    public static final <T> T copyEncrypt(Object source, T target, boolean basicOnly,String suffix, String... ignoreProperties) {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> cls = Hibernate.getClass(source);
        Class<?> actualEditable = Hibernate.getClass(target);
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
        for (PropertyDescriptor targetPd : targetPds) {
            if (basicOnly) {
                Class<?> propertyType = targetPd.getPropertyType();
                if (propertyType.isArray()) {
                    propertyType = propertyType.getComponentType();
                }
                if (!isBasicClass(propertyType)) {
                    // 复杂对象不处理
                    continue;
                }
            }
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || (!ignoreList.contains(targetPd.getName())))) {
                PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(cls, targetPd.getName()+suffix);
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null) {// && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            // if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            // readMethod.setAccessible(true);
                            // }
                            Object value = ReflectionUtils.invokeMethod(readMethod, source);
                            if (value == null) {
                                // 为null的值不处理
                                continue;
                            }
                            setPropertyValue(target, targetPd, value);
                        } catch (Throwable ex) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }
        return target;
    }
    /**
     * 生成32位的uuid.没有短横线"-".
     *
     * @return 没有短横线的UUID
     * @see UUID#randomUUID()
     */
    public static final String uuid() {
        return UUID.randomUUID().toString().replace("-", "");
    }
    /**
     * 判断a是否和b相等.
     * 如果均是空白字符,视为相等; null和空白字符视为相等; 如果a,b均为字符串,区分大小写.忽略字符串前后的空格
     *
     * @param a
     * @param b
     * @return a是否等于b
     */
    public static boolean equals(Object a, Object b) {
        if (Objects.equals(a, b)) {
            return true;
        }
        if (a == null) {
            return b instanceof String && StringUtils.isBlank((String) b);
        }
        if (b == null) {
            return a instanceof String && StringUtils.isBlank((String) a);
        }
        if (a instanceof String && b instanceof String) {
            // 两个均为字符串
            if (StringUtils.isBlank((String) a) && StringUtils.isBlank((String) b)) {
                return true;
            }
            if (((String) a).trim().equals(((String) b).trim())) {
                return true;
            }
        }
        if (a instanceof Date && b instanceof Date) {
            return ((Date) a).getTime() == ((Date) b).getTime();
        }
        if (a instanceof Number && b instanceof Number) {
            return new BigDecimal(a.toString()).equals(new BigDecimal(b.toString()));
        }
        if (a.getClass().equals(b.getClass())) {
            PropertyDescriptor[] pds = getPropertyDescriptors(a.getClass());
            for (PropertyDescriptor pd : pds) {
                if (!equals(getPropertyValue(a, pd), getPropertyValue(b, pd))) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
    /**
     * 判断a是否和b相等.忽略字符串前后的空格
     * 如果均是空白字符,视为相等; null和空白字符视为相等; 如果a,b均为字符串,不区分大小写.
     *
     * @param a
     * @param b
     * @return a是否等于b
     */
    public static boolean equalsIgnoreCase(Object a, Object b) {
        if (a instanceof String && b instanceof String) { // 两个均为字符串
            if (StringUtils.isBlank((String) a) && StringUtils.isBlank((String) b)) {
                return true;
            }
            if (((String) a).trim().equalsIgnoreCase(((String) b).trim())) {
                return true;
            }
        } else {
            return equals(a, b);
        }
        return false;
    }
    /**
     * 比较数组是否相同,如果是字符串,忽略大小写和前后的空格
     *
     * @param a
     * @param b
     * @return
     */
    public static boolean equalsIgnoreCase(Object[] a, Object[] b) {
        if (a == b) {
            return true;
        }
        if (a == null || b == null) {
            return false;
        }
        int length = a.length;
        if (b.length != length) {
            return false;
        }
        for (int i = 0; i < length; i++) {
            if (!equalsIgnoreCase(a[i], b[i])) {
                return false;
            }
        }
        return true;
    }
    /**
     * 比较数组的前length个数据是否相同,如果是字符串,忽略大小写和前后的空格
     *
     * @param a
     * @param b
     * @return
     */
    public static boolean equalsIgnoreCase(Object[] a, Object[] b, int length) {
        if (a.length > length) {
            a = ArrayUtils.subarray(a, 0, length);
        }
        if (b.length > length) {
            b = ArrayUtils.subarray(b, 0, length);
        }
        return equalsIgnoreCase(a, b);
    }
    /**
     * 输出异常的堆栈信息.
     *
     * @param e 异常信息
     * @return
     */
    public static String toString(Throwable e) {
        StringWriter w = new StringWriter();
        PrintWriter pw = new PrintWriter(w);
        e.printStackTrace(pw);
        return w.toString();
    }
    /**
     * 将列名转换为按驼峰命名的属性名.
     *
     * @param columnName 列名
     * @return JAVA驼峰命名规范的属性名
     */
    public static String columnToProperty(String columnName) {
        char[] chars = columnName.toLowerCase().toCharArray();
        StringBuilder buf = new StringBuilder();
        boolean flag = false;
        for (int i = 0, l = chars.length; i < l; i++) {
            char c = chars[i];
            if (c == '_' || c == '-' || c == ' ') { // 下一个单词大写
                flag = buf.length() > 0;
            } else {
                buf.append(flag ? Character.toUpperCase(c) : c);
                flag = false;
            }
        }
        return buf.toString();
    }
    /**
     * java字段名转换为列名.单词之间用下划线_隔开.
     *
     * @param propertyName 属性名
     * @return 转换后的列名
     */
    public static String propertyToColumn(String propertyName) {
        char[] chars = propertyName.toCharArray();
        StringBuilder buf = new StringBuilder();
        for (int i = 0, l = chars.length; i < l; i++) {
            char c = chars[i];
            if (Character.isUpperCase(c)) {
                // 单词大写
                if (buf.length() > 0) {
                    buf.append('_');
                }
            }
            buf.append(c);
        }
        return buf.toString().toLowerCase();
    }
    /**
     * 得到pd属性的列名.
     *
     * @param pd 要得到列名的属性
     * @return 列名
     */
    public static String getColumnName(PropertyDescriptor pd) {
        Column column = pd.getReadMethod() == null ? null : pd.getReadMethod().getAnnotation(Column.class);
        if (column == null && pd.getWriteMethod() != null) {
            // getter方法没有@Column注解,通过setter方法读取
            column = pd.getWriteMethod().getAnnotation(Column.class);
        }
        if (column != null && !column.name().isEmpty()) {
            return column.name();
        }
        return propertyToColumn(pd.getName());
    }
    /**
     * 将Map中的数据读取到到实体对象中.
     *
     * @param data   Map数据.
     * @param entity 实体对象.
     * @return 参数entity本身.
     */
    public static void readValue(Map<?, ?> data, Object entity) {
        data.entrySet().forEach(entry -> {
            String key = entry.getKey().toString();
            Object value = entry.getValue();
            if (!key.contains(String.valueOf(UNDERLINE))) {
                Class<?> clazz = Hibernate.getClass(entity);
                PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, key);
                if (pd != null) {
                    setPropertyValue(entity, pd, value);
                    return;
                }
            }
            setPropertyValueByColumn(entity, key, value);
        });
    }
    /**
     * 将实体转换为map中的数据.只包含不为null的字段
     *
     * @param map
     * @param entity
     * @param <K>    String 或 Object
     */
    @SuppressWarnings({"unchecked"})
    public static <K> void putValue(Map<K, Object> map, Object entity) {
        if (entity == null) {
            return;
        }
        PropertyDescriptor[] pds = getPropertyDescriptors(entity);
        Arrays.stream(pds).forEach(pd -> {
            // String key = pd.getName();
            Object value = getPropertyValue(entity, pd);
            if (value != null) {
                map.put((K) pd.getName(), value);
            }
        });
    }
    /**
     * 将实体对象转换为Map.返回的map中只包含不为null的字段
     *
     * @param entity
     * @param <K>    String 或 Object
     * @return
     */
    public static <K> Map<K, Object> toMap(Object entity) {
        Map<K, Object> map = new HashMap<>();
        putValue(map, entity);
        return map;
    }
    /**
     * 根据列名,将数据value写入到实体entity中.会根据列名的下划线分隔符写入到子对象中.
     *
     * @param entity     实体对象
     * @param columnName SQL读取的列名
     * @param value      要写入的数据值
     * @return 是否有设置值.
     */
    public static boolean setPropertyValueByColumn(Object entity, String columnName, Object value) {
        columnName = StringUtils.strip(columnName, "_");
        Class<?> clazz = Hibernate.getClass(entity);
        String propertyName = columnToProperty(columnName);
        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, propertyName);
        if (pd != null) {
            setPropertyValue(entity, pd, value);
            return true;
        }
        // 可能是包含子对象
        int p = columnName.indexOf("_"); // 以下划线分隔的字段名
        String field;
        while (p > 0) {
            field = columnName.substring(0, p);
            propertyName = columnToProperty(field);
            pd = BeanUtils.getPropertyDescriptor(clazz, propertyName);
            if (pd != null) {
                break;
            }
            p = columnName.indexOf("_", p + 1);
        }
        if (pd == null) {
            return false;
        }
        Object e = getPropertyValue(entity, pd);
        boolean flag = e == null;
        if (flag) {
            e = BeanUtils.instantiateClass(pd.getPropertyType());
        }
        boolean change = setPropertyValueByColumn(e, columnName.substring(p + 1), value);
        if (flag && change) {
            setPropertyValue(entity, pd, e);
        }
        return change;
    }
    /**
     * 将obj对象转换为指定的cls类型.如果没有转换则返回obj
     *
     * @param obj 要转换的数据,一般指从数据库读取的数据
     * @param cls 要转换为的类型,一般为实体类的字段class
     * @return
     */
    public static Object convert(Object obj, Class<?> cls) {
        if (obj == null) {
            return null;
        }
        if (cls.isAssignableFrom(obj.getClass())) {
            return obj;
        }
        if (String.class.equals(cls)) {
            return obj.toString();
        }
        if (Date.class.equals(cls)) {
            return DateUtils.parse(obj.toString());
        }
        if (Double.class.equals(cls) || Double.TYPE.equals(cls) || Float.class.equals(cls) || Float.TYPE.equals(cls) || Long.class.equals(cls)
                || Long.TYPE.equals(cls) || Integer.class.equals(cls) || Integer.TYPE.equals(cls) || Short.class.equals(cls) || Short.TYPE.equals(cls)
                || Byte.class.equals(cls) || Byte.TYPE.equals(cls)) {
            // 数字类型
            BigDecimal bd;
            if (obj instanceof Number) {
                bd = new BigDecimal(obj.toString());
            } else if (obj instanceof Date) {
                bd = new BigDecimal(((Date) obj).getTime());
            } else {
                bd = new BigDecimal(obj.toString());
            }
            if (Double.TYPE.equals(cls)||Double.class.equals(cls)) {
                return bd.doubleValue();
            }
            if (Float.TYPE.equals(cls)||Float.class.equals(cls)) {
                return bd.floatValue();
            }
            if (Long.TYPE.equals(cls)||Long.class.equals(cls)) {
                return bd.longValue();
            }
            if (Integer.TYPE.equals(cls)||Integer.class.equals(cls)) {
                return bd.intValue();
            }
            if (Short.TYPE.equals(cls)||Short.class.equals(cls)) {
                return bd.shortValue();
            }
            if (Byte.TYPE.equals(cls)||Byte.class.equals(cls)) {
                return bd.byteValue();
            }
        }
        if (BigDecimal.class.isAssignableFrom(cls)) {
            // 大数类型
            if (obj instanceof Long || obj instanceof Integer || obj instanceof Short || obj instanceof Byte) {
                return BigDecimal.valueOf(((Number) obj).longValue());
            } else if (obj instanceof Number) {
                return BigDecimal.valueOf(((Number) obj).doubleValue());
            } else if (obj instanceof Date) {
                return BigDecimal.valueOf(((Date) obj).getTime());
            } else {
                return new BigDecimal(obj.toString());
            }
        }
        if (BigInteger.class.isAssignableFrom(cls)) {
            // 大整数类型
            if (obj instanceof Number) {
                return BigInteger.valueOf(((Number) obj).longValue());
            } else if (obj instanceof Date) {
                return BigInteger.valueOf(((Date) obj).getTime());
            } else {
                return new BigInteger(obj.toString());
            }
        }
        return obj;
    }
    /**
     * 将原生数据数组转换为Object[]
     *
     * @param primitives boolean[]/byte[]/short[]/char[]/int[]/long[]/float[]/double[]等类型的数组
     * @return Object[]数组
     */
    public static Object[] toObject(Object primitives) {
        if (primitives == null) {
            return null;
        }
        if (!primitives.getClass().isArray()) {
            return new Object[]{primitives};
        }
        // final Class<?> cls = primitives.getClass().getComponentType();
        if (primitives instanceof boolean[]) {
            return ArrayUtils.toObject((boolean[]) primitives);
        }
        if (primitives instanceof byte[]) {
            return ArrayUtils.toObject((byte[]) primitives);
        }
        if (primitives instanceof short[]) {
            return ArrayUtils.toObject((char[]) primitives);
        }
        if (primitives instanceof char[]) {
            return ArrayUtils.toObject((char[]) primitives);
        }
        if (primitives instanceof int[]) {
            return ArrayUtils.toObject((int[]) primitives);
        }
        if (primitives instanceof long[]) {
            return ArrayUtils.toObject((long[]) primitives);
        }
        if (primitives instanceof float[]) {
            return ArrayUtils.toObject((float[]) primitives);
        }
        if (primitives instanceof double[]) {
            return ArrayUtils.toObject((double[]) primitives);
        }
        return (Object[]) primitives;
    }
    /**
     * 将下划线的字段改为驼峰命名
     *
     * @param param
     * @return
     */
    public static String underlineToCamel(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (c == UNDERLINE) {
                if (++i < len) {
                    sb.append(Character.toUpperCase(param.charAt(i)));
                }
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    /**
     * 首字母转小写
     *
     * @param s
     * @return
     */
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
        }
    }
    /**
     * @param name 转换前的驼峰式命名的字符串
     * @return 转换后下划线大写方式命名的字符串
     */
    public static String humpToDivide(String name, String connStr) {
        StringBuilder result = new StringBuilder();
        if (name != null && name.length() > 0) {
            // 将第一个字符处理成大写
            result.append(name.substring(0, 1).toUpperCase());
            // 循环处理其余字符
            for (int i = 1; i < name.length(); i++) {
                String s = name.substring(i, i + 1);
                // 在大写字母前添加下划线
                if (s.equals(s.toUpperCase()) && !Character.isDigit(s.charAt(0))) {
                    result.append(connStr);
                }
                // 其他字符直接转成大写
                result.append(s);
            }
        }
        return result.toString().toLowerCase(Locale.ENGLISH);
    }
    /**
     * 判断对象是否为null/empty/blan
     *
     * @param value
     * @return
     */
    public static boolean isBlank(Object value) {
        if (value == null) {
            return true;
        }
        if (value instanceof CharSequence) {
            return StringUtils.isBlank((CharSequence) value);
        }
        if (value.getClass().isArray() && Array.getLength(value) < 1) {
            return true;
        }
        if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
            return true;
        }
        if (value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
            return true;
        }
        return false;
    }
    /**
     * 判断对象是否为null/empty/blan
     *
     * @param value
     * @return
     */
    public static boolean isEmpty(Object value) {
        if (value == null) {
            return true;
        }
        if (value instanceof CharSequence) {
            return StringUtils.isEmpty((CharSequence) value);
        }
        if (value.getClass().isArray() && Array.getLength(value) < 1) {
            return true;
        }
        if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
            return true;
        }
        if (value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
            return true;
        }
        return false;
    }
}
src/main/java/com/common/core/utils/DateUtils.java
New file
@@ -0,0 +1,962 @@
package com.common.core.utils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DateUtils {
    /**
     * 年-月-日 时:分:秒 时间格式
     */
    public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * 年-月-日 时:分 时间格式
     */
    public static final String DATE_TIME_MINUTE_FORMAT = "yyyy-MM-dd HH:mm";
    /**
     * 年/月/日 时:分 时间格式
     */
    public static final String DATE_TIME_MINUTE_FORMAT1 = "yyyy/MM/dd HH:mm";
    /**
     * 日/月/年 时:分 时间格式
     */
    public static final String DATE_TIME_MINUTE_FORMAT2 = "dd/MM/yyyy HH:mm";
    /**
     * 2020-08-09T02:35:20.000+0000 格林威治时间
     */
    public static final String DATE_TIME_GL_ST_FORMAT = "yyyy-MM-dd'T'hh:mm:ss.SSSZ";
    public static final String DATE_TIME_GL_ST_FORMAT_ZT = "yyyy-MM-dd'T'HH:mm:ssXXX";
    public static final String DATE_TIME_GL_ST_FORMAT_ZT_1 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    /**
     * 年-月-日 时间格式
     */
    public static final String DATE_FORMAT = "yyyy-MM-dd";
    /**
     * 年-月-日 时间格式
     */
    public static final String YEAR_MONTH_FORMAT = "yyyy-MM-";
    /**
     * 小时格式
     */
    public static final String HOUR_FORMAT = "HH";
    /**
     * 年月日 时间格式
     */
    public static final String DATE_FORMAT_NO_SPACER = "yyyyMMdd";
    /**
     * 月日 时间格式
     */
    public static final String MONTH_DAY_FORMAT_NO_SPACER = "MMdd";
    /**
     * 年月 时间格式
     */
    public static final String YEAR_MONTH_FORMAT_NO_SPACER = "yyyyMM";
    public static final String YEAR_FORMAT = "yyyy";
    public static final String MONTH_FORMAT = "MM";
    public static final String DAY_OF_MONTH_FORMAT = "dd";
    public static final String BJ_TIME_ZONE = "+8";
    /**
     * 默认格式化类型集合
     */
    public static final Map<Pattern, String> SUPPORT_FORMAT_MAP;
    /**
     * 年-月-日T时:分:秒+时:分 时间格式正则匹配表达式
     */
    private static final Pattern DATE_TIME_PATTERN_ZT = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\b\\+[0-9]{2}:[0-9]{2}$");
    /**
     * 年-月-日 时:分:秒 时间格式正则匹配表达式
     */
    private static final Pattern DATE_TIME_PATTERN = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$");
    /**
     * 年-月-日 时:分 时间格式正则匹配表达式
     */
    private static final Pattern DATE_TIME_MINUTE_PATTERN = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$");
    /**
     * 年/月/日 时:分 时间格式正则匹配表达式
     */
    private static final Pattern DATE_TIME_MINUTE_PATTERN1 = Pattern.compile("^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}$");
    /**
     * 日/月年 时:分 时间格式正则匹配表达式
     */
    private static final Pattern DATE_TIME_MINUTE_PATTERN2 = Pattern.compile("[0-9]{2}/[0-9]{2}/^[0-9]{4} [0-9]{2}:[0-9]{2}$");
    /**
     * 年-月-日 时间格式正则匹配表达式
     */
    private static final Pattern DATE_PATTERN = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}$");
    /**
     * 年月日 时间格式正则匹配表达式
     */
    private static final Pattern DATE_PATTERN_NO_SPACER = Pattern.compile("^[0-9]{4}[0-9]{2}[0-9]{2}$");
    /**
     * 月日 时间格式正则匹配表达式
     */
    private static final Pattern MONTH_DAY_PATTERN_NO_SPACER = Pattern.compile("^[0-9]{2}[0-9]{2}$");
    /**
     * 年日 时间格式正则匹配表达式
     */
    private static final Pattern YEAR_MONTH_PATTERN_NO_SPACER = Pattern.compile("^[0-9]{4}[0-9]{2}$");
    /**
     * 3
     */
    private static final Integer NUM_3 = 3;
    /**
     * 5
     */
    private static final Integer NUM_5 = 5;
    static {
        SUPPORT_FORMAT_MAP = new ConcurrentHashMap<Pattern, String>();
        SUPPORT_FORMAT_MAP.put(DATE_TIME_PATTERN, DATE_TIME_FORMAT);
        SUPPORT_FORMAT_MAP.put(DATE_TIME_MINUTE_PATTERN, DATE_TIME_MINUTE_FORMAT);
        SUPPORT_FORMAT_MAP.put(DATE_TIME_MINUTE_PATTERN1, DATE_TIME_MINUTE_FORMAT1);
        SUPPORT_FORMAT_MAP.put(DATE_TIME_MINUTE_PATTERN2, DATE_TIME_MINUTE_FORMAT2);
        SUPPORT_FORMAT_MAP.put(DATE_PATTERN, DATE_FORMAT);
        SUPPORT_FORMAT_MAP.put(DATE_PATTERN_NO_SPACER, DATE_FORMAT_NO_SPACER);
        SUPPORT_FORMAT_MAP.put(YEAR_MONTH_PATTERN_NO_SPACER, YEAR_MONTH_FORMAT_NO_SPACER);
        SUPPORT_FORMAT_MAP.put(MONTH_DAY_PATTERN_NO_SPACER, MONTH_DAY_FORMAT_NO_SPACER);
        SUPPORT_FORMAT_MAP.put(DATE_TIME_PATTERN_ZT, DATE_TIME_GL_ST_FORMAT_ZT);
    }
    /**
     * 字符串转日期类型
     *
     * @param dateString
     * @return
     */
    public static Date convert(String dateString) {
        Date date = null;
        if (StringUtils.isEmpty(dateString)) {
            return null;
        }
        for (Pattern pattern : SUPPORT_FORMAT_MAP.keySet()) {
            Matcher matcher = pattern.matcher(dateString);
            if (matcher.matches()) {
                try {
                    String format = SUPPORT_FORMAT_MAP.get(pattern);
                    SimpleDateFormat sdf = new SimpleDateFormat(format);
                    sdf.setLenient(false);
                    date = sdf.parse(dateString);
                } catch (ParseException ignored) {
                }
                break;
            }
        }
        return date;
    }
    /**
     * 带时区字符串转日期类型
     *
     * @param dateString
     * @return
     */
    public static Date timeZoneConvert(String dateString) {
        Date date = null;
        if (StringUtils.isNotBlank(dateString)) {
            Matcher matcher = DATE_TIME_PATTERN_ZT.matcher(dateString);
            if (matcher.matches()) {
                try {
                    String format = SUPPORT_FORMAT_MAP.get(DATE_TIME_PATTERN_ZT);
                    SimpleDateFormat sdf = new SimpleDateFormat(format);
                    sdf.setLenient(false);
                    date = sdf.parse(dateString);
                } catch (ParseException ignored) {
                }
            }
        }
        return date;
    }
    /**
     * 根据格式,转化字符串为日期
     *
     * @param dateString
     * @param format
     * @return
     */
    public static Date convert(String dateString, String format) {
        if (StringUtils.isBlank(dateString)) {
            return null;
        }
        boolean matches = false;
        for (String fmt : SUPPORT_FORMAT_MAP.values()) {
            if (fmt.equals(format)) {
                matches = true;
                break;
            }
        }
        if (!matches) {
            throw new IllegalArgumentException(format + " is not a valid date format");
        }
        Date date = null;
        try {
            date = new SimpleDateFormat(format).parse(dateString);
        } catch (ParseException ignored) {
        }
        return date;
    }
    /**
     * 根据格式,转化字符串为日期
     *
     * @param dateString
     * @param format
     * @return
     */
    public static Date convertSimple(String dateString, String format) {
        if (StringUtils.isBlank(dateString)) {
            return null;
        }
        Date date = null;
        try {
            date = new SimpleDateFormat(format).parse(dateString);
        } catch (ParseException ignored) {
        }
        return date;
    }
    /**
     * 根据时间字符串解析成Date对象,该方法会自适应时间字符串格式
     *
     * @param dateText 时间字符串
     */
    public static Date parse(String dateText) {
        return DateUtils.convert(dateText);
    }
    /**
     * 根据时间字符串,解析成指定格式的的Date对象
     *
     * @param dateText 时间字符串
     * @param format   指定解析格式
     */
    public static Date parse(String dateText, String format) {
        return DateUtils.convert(dateText, format);
    }
    /**
     * 格式化日期为yyyy-MM-dd HH:mm:ss格式字符串
     *
     * @param date 日期对象
     */
    public static String format(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        simpleDateFormat.applyPattern(DATE_TIME_FORMAT);
        return simpleDateFormat.format(date);
    }
    /**
     * 格式化日期为指定格式字符串
     *
     * @param date 日期对象
     */
    public static String format(Date date, String pattern) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        simpleDateFormat.applyPattern(pattern);
        return simpleDateFormat.format(date);
    }
    /**
     * 格式化日期为指定格式字符串
     *
     * @param date 日期对象
     */
    public static String formatToUtc(Date date, String pattern) {
        SimpleDateFormat df = new SimpleDateFormat(pattern);
        df.setTimeZone(new SimpleTimeZone(0, "UTC"));
        return df.format(date);
    }
    /**
     * 格式化日期为yyyy-MM-dd HH:mm:ss格式字符串
     *
     * @param timestamp 时间戳
     */
    public static String format(long timestamp) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        simpleDateFormat.applyPattern(DATE_TIME_FORMAT);
        return simpleDateFormat.format(DateUtils.newDate(timestamp));
    }
    /**
     * 格式化日期为指定格式字符串
     *
     * @param timestamp 时间戳
     */
    public static String format(long timestamp, String pattern) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        simpleDateFormat.applyPattern(pattern);
        return simpleDateFormat.format(DateUtils.newDate(timestamp));
    }
    /**
     * 格式化日期为时间戳
     */
    public static long format(String dateText) {
        if (StringUtils.isEmpty(dateText)) {
            return 0L;
        }
        Date date=DateUtils.convert(dateText);
        return date==null ? 0L : date.getTime();
    }
    /**
     * 获取当前时间的日期对象
     */
    public static Date now() {
        return DateUtils.newDate(DateUtils.nowTimestamp());
    }
    /**
     * 获取当前时间时间戳
     */
    public static long nowTimestamp() {
        return System.currentTimeMillis();
    }
    /**
     * 根据时间戳创建日期对象
     *
     * @param timestamp 时间戳
     */
    public static Date newDate(long timestamp) {
        return new Date(timestamp);
    }
    /**
     * 根据指定时间创建日期对象
     *
     * @param year        年
     * @param month       月
     * @param day         日
     * @param hour        时
     * @param minute      分
     * @param second      秒
     * @param millisecond 毫秒
     */
    public static Date newDate(int year, int month, int day, int hour, int minute, int second, int millisecond) {
        DateTime dateTime = new DateTime(year, month, day, hour, minute, second, millisecond);
        return dateTime.toDate();
    }
    /**
     * 根据指定时间创建日期对象
     *
     * @param year   年
     * @param month  月
     * @param day    日
     * @param hour   时
     * @param minute 分
     * @param second 秒
     */
    public static Date newDate(int year, int month, int day, int hour, int minute, int second) {
        return DateUtils.newDate(year, month, day, hour, minute, second, 0);
    }
    /**
     * 根据指定时间创建日期对象
     *
     * @param year   年
     * @param month  月
     * @param day    日
     * @param hour   时
     * @param minute 分
     */
    public static Date newDate(int year, int month, int day, int hour, int minute) {
        return DateUtils.newDate(year, month, day, hour, minute, 0, 0);
    }
    /**
     * 根据指定时间创建日期对象
     *
     * @param year  年
     * @param month 月
     * @param day   日
     * @param hour  时
     */
    public static Date newDate(int year, int month, int day, int hour) {
        return DateUtils.newDate(year, month, day, hour, 0, 0, 0);
    }
    /**
     * 根据指定时间创建日期对象
     *
     * @param year  年
     * @param month 月
     * @param day   日
     */
    public static Date newDate(int year, int month, int day) {
        return DateUtils.newDate(year, month, day, 0, 0, 0, 0);
    }
    /**
     * 日期加减 年月日时分秒毫秒数 正数加负数减
     *
     * @param date         日期
     * @param years        加减年数
     * @param months       加减月数
     * @param days         加减天数
     * @param hours        加减小时数
     * @param minutes      加减分钟数
     * @param seconds      加减秒数
     * @param milliseconds 加减毫秒数
     */
    public static Date add(Date date, int years, int months, int days, int hours, int minutes, int seconds, int milliseconds) {
        return DateUtils.add(date.getTime(), years, months, days, hours, minutes, seconds, milliseconds);
    }
    /**
     * 日期加减 年月日时分秒毫秒数 正数加负数减
     *
     * @param timestamp    时间戳
     * @param years        加减年数,如:1、-1
     * @param months       加减月数,如:1、-1
     * @param days         加减天数,如:1、-1
     * @param hours        加减小时数,如:1、-1
     * @param minutes      加减分钟数,如:1、-1
     * @param seconds      加减秒数,如:1、-1
     * @param milliseconds 加减毫秒数,如:1、-1
     */
    public static Date add(long timestamp, int years, int months, int days, int hours, int minutes, int seconds, int milliseconds) {
        int zero = 0;
        DateTime dateTime = new DateTime(timestamp);
        if (zero != years) {
            dateTime = dateTime.plusYears(years);
        }
        if (zero != months) {
            dateTime = dateTime.plusMonths(months);
        }
        if (zero != days) {
            dateTime = dateTime.plusDays(days);
        }
        if (zero != hours) {
            dateTime = dateTime.plusHours(hours);
        }
        if (zero != minutes) {
            dateTime = dateTime.plusMinutes(minutes);
        }
        if (zero != seconds) {
            dateTime = dateTime.plusSeconds(seconds);
        }
        if (zero != milliseconds) {
            dateTime = dateTime.plusMillis(milliseconds);
        }
        return dateTime.toDate();
    }
    /**
     * 获取本周的第一天
     *
     * @return Long
     **/
    public static Long getWeekStart(long timestamp) {
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(timestamp);
        Integer date = cal.get(Calendar.DATE);
        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        if (cal.get(Calendar.DATE) > date) {
            cal.add(Calendar.DATE, -7);
        }
        Date time = cal.getTime();
        return format(new SimpleDateFormat("yyyy-MM-dd").format(time) + " 00:00:00");
    }
    /**
     * 获取本周的最后一天
     *
     * @return Long
     **/
    public static Long getWeekEnd(long timestamp) {
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(timestamp);
        Integer date = cal.get(Calendar.DATE);
        cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        if (cal.get(Calendar.DATE) < date) {
            cal.add(Calendar.DATE, 7);
        }
        Date time = cal.getTime();
        return format(new SimpleDateFormat("yyyy-MM-dd").format(time) + " 23:59:59");
    }
    /**
     * 获取本天的最后一秒
     *
     * @return Long
     **/
    public static Date getDayEnd(Date date) {
        if (date == null) {
            return null;
        }
        return parse(new SimpleDateFormat("yyyy-MM-dd").format(date.getTime()) + " 23:59:59");
    }
    /**
     * 获取本天的第一秒
     *
     * @return Long
     **/
    public static Date getDayStart(Date date) {
        if (date == null) {
            return null;
        }
        return parse(new SimpleDateFormat("yyyy-MM-dd").format(date.getTime()) + " 00:00:00");
    }
    /**
     * 根据时间戳获取版本号,Calendar取周是从周日开始计算,需要进行修改为以周一为准
     *
     * @param timestamp 时间戳
     * @return 版本号
     */
    public static Integer getDateVersion(long timestamp) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timestamp);
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setMinimalDaysInFirstWeek(4);
        return calendar.get(Calendar.YEAR) * 1000 + calendar.get(Calendar.WEEK_OF_YEAR);
    }
    /**
     * 获取当前时间的版本号
     *
     * @return 版本号
     */
    public static Integer getNowVersion() {
        return getDateVersion(nowTimestamp());
    }
    /**
     * 获取年月周
     *
     * @return String
     **/
    public static Map<String, String> getYearMonthWeekStr(long timestamp) {
        // 当月第一天
        Calendar firstDay = Calendar.getInstance();
        firstDay.setTimeInMillis(timestamp);
        firstDay.set(Calendar.DAY_OF_MONTH, 1);
        // 当月最后一天
        Calendar lastDay = Calendar.getInstance();
        lastDay.setTimeInMillis(timestamp);
        lastDay.add(Calendar.MONTH, 1);
        lastDay.set(Calendar.DAY_OF_MONTH, 0);
        // 第一个周日
        Calendar firsSunday = Calendar.getInstance();
        firsSunday.setTimeInMillis(firstDay.getTimeInMillis());
        firsSunday.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        if (firsSunday.getTimeInMillis() != firstDay.getTimeInMillis()) {
            firsSunday.add(Calendar.DATE, 7);
        }
        // 最后一个周一
        Calendar lastMonday = Calendar.getInstance();
        lastMonday.setTimeInMillis(lastDay.getTimeInMillis());
        lastMonday.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        if (lastMonday.getTimeInMillis() > lastDay.getTimeInMillis()) {
            lastMonday.add(Calendar.DATE, -7);
        }
        String year = "";
        String month = "";
        String week = "";
        DateFormat df = new SimpleDateFormat("MMM", Locale.ENGLISH);
        if (firsSunday.getTimeInMillis() >= timestamp) {
            // 三天以上算本月
            if (firsSunday.get(Calendar.DATE) > NUM_3) {
                Calendar cal = Calendar.getInstance();
                cal.setTimeInMillis(timestamp);
                year = cal.get(Calendar.YEAR) + "";
                month = df.format(cal.getTime());
                week = cal.get(Calendar.WEEK_OF_MONTH) + "th Week";
            } else {
                // 三天以内算上月
                // 上月第一天
                Calendar preFirstDay = Calendar.getInstance();
                preFirstDay.setTimeInMillis(timestamp);
                preFirstDay.set(Calendar.DAY_OF_MONTH, 1);
                preFirstDay.add(Calendar.MONTH, -1);
                // 上月第一个周日
                Calendar preFirsSunday = Calendar.getInstance();
                preFirsSunday.setTimeInMillis(preFirstDay.getTimeInMillis());
                preFirsSunday.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
                if (preFirsSunday.getTimeInMillis() != preFirstDay.getTimeInMillis()) {
                    preFirsSunday.add(Calendar.DATE, 7);
                }
                // 上月最后一天
                Calendar preLastDay = Calendar.getInstance();
                preLastDay.setTimeInMillis(firstDay.getTimeInMillis());
                preLastDay.add(Calendar.DATE, -1);
                year = preLastDay.get(Calendar.YEAR) + "";
                month = df.format(preLastDay.getTime());
                if (preFirsSunday.get(Calendar.DATE) > NUM_3) {
                    week = preLastDay.get(Calendar.WEEK_OF_MONTH) + "th Week";
                } else {
                    week = (preLastDay.get(Calendar.WEEK_OF_MONTH) - 1) + "th Week";
                }
            }
        } else if (lastMonday.getTimeInMillis() <= timestamp) {
            // 本月在最后一周占4天及以上
            if (lastDay.get(Calendar.DAY_OF_WEEK) >= NUM_5 || lastDay.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                Calendar cal = Calendar.getInstance();
                cal.setTimeInMillis(timestamp);
                year = cal.get(Calendar.YEAR) + "";
                month = df.format(cal.getTime());
                if (firsSunday.get(Calendar.DATE) > NUM_3) {
                    week = cal.get(Calendar.WEEK_OF_MONTH) + "th Week";
                } else {
                    week = (cal.get(Calendar.WEEK_OF_MONTH) - 1) + "th Week";
                }
            } else {
                // 下月第一天
                Calendar nextFirstDay = Calendar.getInstance();
                nextFirstDay.setTimeInMillis(timestamp);
                nextFirstDay.add(Calendar.MONTH, 1);
                nextFirstDay.set(Calendar.DAY_OF_MONTH, 1);
                year = nextFirstDay.get(Calendar.YEAR) + "";
                month = df.format(nextFirstDay.getTime());
                week = nextFirstDay.get(Calendar.WEEK_OF_MONTH) + "th Week";
            }
        } else {
            Calendar cal = Calendar.getInstance();
            cal.setTimeInMillis(timestamp);
            year = cal.get(Calendar.YEAR) + "";
            month = df.format(cal.getTime());
            if (firsSunday.get(Calendar.DATE) > NUM_3) {
                week = cal.get(Calendar.WEEK_OF_MONTH) + "th Week";
            } else {
                week = (cal.get(Calendar.WEEK_OF_MONTH) - 1) + "th Week";
            }
        }
        Map<String, String> result = new HashMap<String, String>();
        result.put("year", year);
        result.put("month", month);
        result.put("week", week);
        return result;
    }
    /**
     * 添加年份
     *
     * @param date
     * @param years
     * @return
     */
    public static Date addYears(Date date, int years) {
        return DateUtils.addYears(date.getTime(), years);
    }
    /**
     * 添加年份
     *
     * @param timestamp
     * @param years
     * @return
     */
    public static Date addYears(long timestamp, int years) {
        return DateUtils.add(timestamp, years, 0, 0, 0, 0, 0, 0);
    }
    /**
     * 添加月份
     *
     * @param date
     * @param months
     * @return
     */
    public static Date addMonths(Date date, int months) {
        return DateUtils.addMonths(date.getTime(), months);
    }
    /**
     * 添加月份
     *
     * @param timestamp
     * @param months
     * @return
     */
    public static Date addMonths(long timestamp, int months) {
        return DateUtils.add(timestamp, 0, months, 0, 0, 0, 0, 0);
    }
    /**
     * 添加日期
     *
     * @param date
     * @param days
     * @return
     */
    public static Date addDays(Date date, int days) {
        return DateUtils.addDays(date.getTime(), days);
    }
    /**
     * 添加日期
     *
     * @param timestamp
     * @param days
     * @return
     */
    public static Date addDays(long timestamp, int days) {
        return DateUtils.add(timestamp, 0, 0, days, 0, 0, 0, 0);
    }
    /**
     * 添加小时
     *
     * @param date
     * @param hours
     * @return
     */
    public static Date addHours(Date date, int hours) {
        return DateUtils.addHours(date.getTime(), hours);
    }
    /**
     * 添加小时
     *
     * @param timestamp
     * @param hours
     * @return
     */
    public static Date addHours(long timestamp, int hours) {
        return DateUtils.add(timestamp, 0, 0, 0, hours, 0, 0, 0);
    }
    /**
     * 添加分钟
     *
     * @param date
     * @param minutes
     * @return
     */
    public static Date addMinutes(Date date, int minutes) {
        return DateUtils.addMinutes(date.getTime(), minutes);
    }
    /**
     * 添加分钟
     *
     * @param timestamp
     * @param minutes
     * @return
     */
    public static Date addMinutes(long timestamp, int minutes) {
        return DateUtils.add(timestamp, 0, 0, 0, 0, minutes, 0, 0);
    }
    /**
     * 获取当前月份
     *
     * @return
     */
    public static int getCurrentMonth() {
        DateTime dateTime = DateTime.now();
        return dateTime.getMonthOfYear();
    }
    /**
     * 通过时间秒毫秒数判断两个时间的间隔
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        int days = (int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24));
        return days;
    }
    /**
     * 取两个日期的中间日期,默认取大
     *
     * @param start
     * @param end
     * @return
     */
    public static Date getMeddleDate(Date start, Date end) {
        int days = (int) ((end.getTime() - start.getTime()) / (1000 * 3600 * 24));
        return DateUtils.addDays(start, (int) Math.floor(Double.valueOf(days) / 2));
    }
    /**
     * 通过时间秒毫秒数判断两个时间的间隔
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int differentSeconds(Date date1, Date date2) {
        int days = (int) ((date2.getTime() - date1.getTime()) / (1000));
        return days;
    }
    /**
     * 按日期递增规则生成分区列表
     *
     * @param start
     * @param end
     * @return
     */
    public static List<String> generateDayList(String start, String end, String pattern) {
        List<String> partitionList = new ArrayList<String>();
        partitionList.add(start);
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Date startDate;
        Date endDate;
        try {
            startDate = sdf.parse(start);
            endDate = sdf.parse(end);
        } catch (Exception e) {
            return null;
        }
        Calendar startCal = Calendar.getInstance();
        Calendar endCal = Calendar.getInstance();
        startCal.setTime(startDate);
        endCal.setTime(endDate);
        while (startCal.before(endCal)) {
            startCal.add(Calendar.DAY_OF_MONTH, 1);
            partitionList.add(sdf.format(startCal.getTime()));
        }
        return partitionList;
    }
    /**
     * 格式化utc时间
     *
     * @param utcDate
     * @return
     */
    public static String formatUtcDate(String utcDate) {
        DateFormat df = new SimpleDateFormat(DateUtils.DATE_TIME_GL_ST_FORMAT);
        DateFormat df2 = new SimpleDateFormat(DateUtils.DATE_TIME_FORMAT);
        Date date = null;
        try {
            date = df.parse(utcDate);
        } catch (ParseException e) {
            return utcDate;
        }
        return df2.format(date);
    }
    /**
     * 变更日期字符串格式
     *
     * @return
     */
    public static String convertDateStrPattern(String dateStr, String sourcePattern, String targetPattern) {
        DateFormat df = new SimpleDateFormat(sourcePattern);
        DateFormat df2 = new SimpleDateFormat(targetPattern);
        Date date = null;
        try {
            date = df.parse(dateStr);
        } catch (ParseException e) {
            return null;
        }
        return df2.format(date);
    }
    /**
     * 格式化utc时间
     *
     * @param localDate
     * @return
     */
    public static Date localToUTC(Date localDate) {
        long localTimeInMillis = localDate.getTime();
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(localTimeInMillis);
        int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
        int dstOffset = calendar.get(Calendar.DST_OFFSET);
        calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));
        Date utcDate = new Date(calendar.getTimeInMillis());
        return utcDate;
    }
    /**
     * @param date1
     * @param date2
     * @return
     */
    public static int differentDays(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        int day1 = cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);
        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);
        if (year1 != year2) {
            int timeDistance = 0;
            for (int i = year1; i < year2; i++) {
                if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {//闰年
                    timeDistance += 366;
                } else {//不是闰年
                    timeDistance += 365;
                }
            }
            return timeDistance + (day2 - day1);
        } else { //不同年
            System.out.println("判断day2 - day1 : " + (day2 - day1));
            return day2 - day1;
        }
    }
    public static Long timestamp2dateLong(Long timestamp) {
        return DateUtils.format(DateUtils.format(timestamp).substring(0, 10));
    }
    public static boolean isValidDate(String str, String pattern) {
        boolean convertSuccess = true;
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        try {
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期,比如2007/02/29会被接受,并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
        } catch (ParseException e) {
            convertSuccess = false;
        }
        return convertSuccess;
    }
}
src/main/java/com/common/core/utils/DesensitiveUtils.java
New file
@@ -0,0 +1,217 @@
package com.common.core.utils;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
/**
 * @description: 脱敏工具类
 * @author: holden
 * @time: 2022-01-20 09:10
 */
public class DesensitiveUtils {
    private DesensitiveUtils() {
    }
    /**
     * 扫描方法注解,脱敏
     *
     * @param obj
     */
    public static void format(Object obj) {
        DesensitiveUtils.formatMethod(obj);
    }
    /**
     *  判断对象类型,进行分类处理
     *
     * @param obj   需要反射对象
     */
    private static void formatMethod(Object obj) {
        if (obj == null || isPrimitive(obj.getClass())) {
            return;
        }
        if (obj.getClass().isArray()) {
            for (Object object : (Object[]) obj) {
                formatMethod(object);
            }
        } else if (Collection.class.isAssignableFrom(obj.getClass())) {
            for (Object o : ((Collection) obj)) {
                formatMethod(o);
            }
        } else if (Map.class.isAssignableFrom(obj.getClass())) {
            for (Object o : ((Map) obj).values()) {
                formatMethod(o);
            }
        } else {
            objFormat(obj);
        }
    }
    /**
     * 只有对象才格式化数据
     *
     * @param obj
     */
    private static void objFormat(Object obj) {
        for (Field field : obj.getClass().getDeclaredFields()) {
            if (isPrimitive(field.getType())||field.getType().isArray()) {
                fieldSetSensitiveValue(obj, field);
            }else{
                //对象类型进行下一级处理
                ReflectionUtils.makeAccessible(field);
                Object fieldValue = ReflectionUtils.getField(field, obj);
                if (fieldValue != null) {
                    formatMethod(fieldValue);
                }
            }
        }
    }
    /**
     * 进行脱敏处理的方法
     * @param obj
     * @param field
     */
    private static void fieldSetSensitiveValue(Object obj, Field field)  {
        DesensitiveInfo annotation = field.getAnnotation(DesensitiveInfo.class);
        if (annotation!=null){
            ReflectionUtils.makeAccessible(field);
            Object fieldValue = ReflectionUtils.getField(field, obj);
            String padStr = annotation.padStr();
            DesensitiveType type = annotation.value();
            Integer padSize = type.getPadSize();
            if (fieldValue!=null && !"".equals(fieldValue.toString())){
                if(DesensitiveType.EMAIL.name().equals(type.name())){
                    if(String.class.isAssignableFrom(field.getType())) {
                        ReflectionUtils.setField(field, obj, generatePad(padSize, padStr) + "@company.com");
                    }
                    if(field.getType().isArray()){
                        ArrayList<String> list=new ArrayList();
                        for (String s : (String[])fieldValue) {
                            // 对空字符原样处理
                            if(s==null || s.isEmpty()){
                                list.add(s);
                            }else {
                                list.add(generatePad(padSize, padStr) + "@company.com");
                            }
                        }
                        ReflectionUtils.setField(field, obj, list.toArray(new String[0]));
                    }
                }else {
                    if(String.class.isAssignableFrom(field.getType())) {
                        ReflectionUtils.setField(field, obj, generatePad(padSize, padStr));
                    }
                    if(field.getType().isArray()){
                        ArrayList<String> list=new ArrayList();
                        for (String s : (String[])fieldValue) {
                            // 对空字符原样处理
                            if(s==null || s.isEmpty()){
                                list.add(s);
                            }else {
                                list.add(generatePad(padSize, padStr));
                            }
                        }
                        ReflectionUtils.setField(field, obj, list.toArray(new String[0]));
                    }
                }
            }
//            else{
//                if(DesensitiveType.EMAIL.name().equals(type.name())){
//                    if(String.class.isAssignableFrom(field.getType())) {
//                        ReflectionUtils.setField(field, obj, generatePad(padSize, padStr) + "@company.com");
//                    }
//                    if(field.getType().isArray()){
//                        ArrayList<String> list=new ArrayList();
//                        list.add(generatePad(padSize, padStr) + "@company.com");
//                        ReflectionUtils.setField(field, obj, list.toArray(new String[0]));
//                    }
//                }else {
//                    if(String.class.isAssignableFrom(field.getType())) {
//                        ReflectionUtils.setField(field, obj, generatePad(padSize, padStr));
//                    }
//                    if(field.getType().isArray()){
//                        ArrayList<String> list=new ArrayList();
//                        list.add(generatePad(padSize, padStr));
//                        ReflectionUtils.setField(field, obj, list.toArray(new String[0]));
//                    }
//                }
//            }
        }
    }
    public static String getFieldDesensitive(Object t, Field field){
        DesensitiveInfo annotation = field.getAnnotation(DesensitiveInfo.class);
        if (annotation!=null){
            DesensitiveType type = annotation.value();
            String padStr = annotation.padStr();
            Integer padSize = type.getPadSize();
            return generatePad(padSize, padStr);
        }
        return null;
    }
    public static String getFieldDesensitive(DesensitiveType field){
        String padStr = "*";
        Integer padSize = field.getPadSize();
        if(DesensitiveType.EMAIL.name().equals(field.name())) {
            return generatePad(padSize, padStr)+"@company.com";
        }else{
            return generatePad(padSize, padStr);
        }
    }
    public static String getFieldDesensitive(Object t, DesensitiveType field){
        if(null == t || "".equals(t.toString())){
            return "";
        }
        String padStr = "*";
        Integer padSize = field.getPadSize();
        if(DesensitiveType.EMAIL.name().equals(field.name())) {
            return generatePad(padSize, padStr)+"@company.com";
        }else{
            return generatePad(padSize, padStr);
        }
    }
    public static String generatePad(Integer padSize,String pad){
        String padstr = "";
        for(int i = 0; i < padSize; i++){
            padstr += pad;
        }
        return padstr;
    }
    /**
     * 基本数据类型和String类型判断
     *
     * @param clz
     * @return
     */
    public static boolean isPrimitive(Class<?> clz) {
        try {
            if (String.class.isAssignableFrom(clz) || Date.class.isAssignableFrom(clz)|| BigDecimal.class.isAssignableFrom(clz) || LocalDateTime.class.isAssignableFrom(clz)
                    || LocalDate.class.isAssignableFrom(clz)  || LocalTime.class.isAssignableFrom(clz)  || Year.class.isAssignableFrom(clz)
                    || YearMonth.class.isAssignableFrom(clz)  || Month.class.isAssignableFrom(clz) || clz.isPrimitive()) {
                return true;
            } else {
                return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
            }
        } catch (Exception e) {
            return false;
        }
    }
}
src/main/java/com/common/core/utils/DistributedLock.java
New file
@@ -0,0 +1,36 @@
package com.common.core.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class DistributedLock {
    @Autowired
    private RedisTemplate redisTemplate;
    public boolean getLock(String lockId, long millisecond) {
        Boolean success = redisTemplate.opsForValue().setIfAbsent(lockId, "lock",
                millisecond, TimeUnit.MILLISECONDS);
        return success != null && success;
    }
    public boolean waitLock(String lockId, long millisecond) {
        do {
            if(this.getLock(lockId,millisecond)){
                return true;
            }else{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }while(true);
    }
    public void releaseLock(String lockId) {
        redisTemplate.delete(lockId);
    }
}
src/main/java/com/common/core/utils/DynamicBean.java
New file
@@ -0,0 +1,43 @@
package com.common.core.utils;
import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;
import java.util.Map;
class DynamicBean {
    private Object target;
    private BeanMap beanMap;
    public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
        this.target = generateBean(superclass, propertyMap);
        this.beanMap = BeanMap.create(this.target);
    }
    public void setValue(String property, Object value) {
        beanMap.put(property, value);
    }
    public Object getValue(String property) {
        return beanMap.get(property);
    }
    public Object getTarget() {
        return this.target;
    }
    /**
     * 根据属性生成对象
     */
    private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
        BeanGenerator generator = new BeanGenerator();
        if (null != superclass) {
            generator.setSuperclass(superclass);
        }
        BeanGenerator.addProperties(generator, propertyMap);
        return generator.create();
    }
}
src/main/java/com/common/core/utils/GsonUtils.java
New file
@@ -0,0 +1,69 @@
package com.common.core.utils;
import com.common.core.exception.BizException;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringEscapeUtils;
/**
 * @Author: 廖振钦
 * @description: gonsutils
 * @date 2022-01-06:10:45
 * */
@Slf4j
public class GsonUtils {
    public static Gson g = new Gson();
    public static <T> T fromJson(String json, Class<? extends  T> t, int layer){
        if(layer < 5) {
            try {
                if(layer == 0){
                    return g.fromJson(json, t);
                }else if (layer == 1){
                    String tmp_json = StringEscapeUtils.unescapeJson(StringEscapeUtils.unescapeHtml4(json));
                    return  g.fromJson(tmp_json, t);
                }else  if (layer == 2){
                    String tmp_json = StringEscapeUtils.unescapeJson(StringEscapeUtils.unescapeHtml4(json));
                    String overjson = toJsonString(tmp_json);
                    return g.fromJson(overjson, t);
                }else  if (layer == 3){
                    String overjson=json.replaceAll("\"}\",", "\"/}\",");
                    return g.fromJson(overjson, t);
                }else{
                    String overjson = toJsonString(json);
                    return  g.fromJson(overjson, t);
                }
            } catch (Exception e) {
                return fromJson(json, t, layer + 1);
            }
        }else{
            log.error("GSON解析异常:"+json);
            throw new BizException("JSON解析异常");
        }
    }
    private static String toJsonString(String s) {
        char[] tempArr = s.toCharArray();
        int tempLength = tempArr.length;
        for (int i = 0; i < tempLength; i++) {
            if (tempArr[i] == ':' && tempArr[i + 1] == '"') {
                for (int j = i + 2; j < tempLength; j++) {
                    if (tempArr[j] == '"') {
                        if (tempArr[j + 1] != ',' && tempArr[j + 1] != '}') {
                            tempArr[j] = '”'; // 将value中的 双引号替换为中文双引号
                        } else if (tempArr[j + 1] == ',' || tempArr[j + 1] == '}') {
                            break;
                        }
                    }
                }
            }
        }
        return new String(tempArr);
    }
    public static String toJson(Object o){
        return g.toJson(g);
    }
}
src/main/java/com/common/core/utils/IdUtils.java
New file
@@ -0,0 +1,202 @@
package com.common.core.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
 * Twitter_Snowflake<br>
 * SnowFlake的结构如下(每部分用-分开):<br>
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
 * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
 * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
 * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
 * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
 * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
 * 加起来刚好64位,为一个Long型。<br>
 * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
 */
@Component
public class IdUtils {
    // ==============================Fields===========================================
    /** 开始时间截 (2015-01-01) */
    private static final long twepoch = 1420041600000L;
    /** 机器id所占的位数 */
    private static final long workerIdBits = 5L;
    /** 数据标识id所占的位数 */
    private static final long datacenterIdBits = 5L;
    /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private static final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    /** 支持的最大数据标识id,结果是31 */
    private static final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    /** 序列在id中占的位数 */
    private static final long sequenceBits = 12L;
    /** 机器ID向左移12位 */
    private static final long workerIdShift = sequenceBits;
    /** 数据标识id向左移17位(12+5) */
    private static final long datacenterIdShift = sequenceBits + workerIdBits;
    /** 时间截向左移22位(5+5+12) */
    private static final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
    private static final long sequenceMask = -1L ^ (-1L << sequenceBits);
    /** 工作机器ID(0~31) */
    @Value("${custom.worker-id}")
    private long workerId;
    /** 数据中心ID(0~31) */
    @Value("${custom.datacenter-id}")
    private long datacenterId;
    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;
    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;
    public IdUtils(){
    }
    //==============================Constructors=====================================
    /**
     * 构造函数
     * @param workerId 工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31)
     */
    public IdUtils(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    // ==============================Methods==========================================
    /**
     * 获得下一个ID (该方法是线程安全的)
     * @return SnowflakeId
     */
    public synchronized String nextId() {
        long timestamp = timeGen();
        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }
        //上次生成ID的时间截
        lastTimestamp = timestamp;
        System.out.println(timestamp);
        //移位并通过或运算拼到一起组成64位的ID
        long nextId= ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
        return String.valueOf(nextId);
    }
    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    /**
     * 返回以毫秒为单位的当前时间
     * @return
     */
    public long inverseDate(Long id) {
        return (id >> timestampLeftShift) + twepoch;
    }
    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
    //==============================Test=============================================
    /** 测试 */
    public static void main(String[] args) {
        IdUtils idWorker = new IdUtils(0, 0);
//        System.out.println(idWorker.inverseDate(Long.parseLong("1645595070821")));
//        System.out.println(idWorker.inverseDate(Long.parseLong("1645595070820")));
//        System.out.println(idWorker.inverseDate(Long.parseLong("1645595070819")));
//        System.out.println(idWorker.inverseDate(Long.parseLong("1645595070818")));
//        System.out.println(idWorker.inverseDate(Long.parseLong("1645595070801")));
//        System.out.println(idWorker.inverseDate(Long.parseLong("1445595060102")));
//        for (int i = 0; i < 100; i++) {
//            String id = idWorker.nextId();
//            System.out.println("id="+id);
//            System.out.println("inverse_time="+idWorker.inverseDate((Long.valueOf(id))));
//        }
//        IdUtils idWorker = new IdUtils(0, 0);
//
//        for (int i = 0; i < 100; i++) {
//            String id = idWorker.nextId();
////            System.out.println(Long.toBinaryString(Long.valueOf(id)));
////            System.out.println(id);
//        }
        String [] s = new String[]{
                "1645595070820",
                "1645595070821",
                "1645595070822",
                "1645595070819",
                "1645595070823"};
        IdUtils idUtils = new IdUtils();
        List<String>  sortkeysets =  Arrays.stream(s).sorted(new Comparator<String>(){
            @Override
            public int compare(String o1, String o2) {
                Long o1L = Long.parseLong(o1);
                Long o2L = Long.parseLong(o2);
                Long v = o1L-o2L; //倒序
                return v.intValue();
            }
        }).collect(Collectors.toList());
        sortkeysets.forEach( e -> System.out.println(e));
    }
}
src/main/java/com/common/core/utils/JsonUtils.java
New file
@@ -0,0 +1,75 @@
package com.common.core.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
/**
 * Json转换工具类
 */
@Slf4j
public class JsonUtils {
    public static final ObjectMapper objectMapper = new ObjectMapper();
    /**
     * 字符串转对象
     *
     * @param jsonStr json字符串
     * @param clazz   转换类型
     * @param <T>
     * @return
     */
    public static <T> T string2Object(String jsonStr, Class<T> clazz) throws IOException {
        return objectMapper.readValue(jsonStr, clazz);
    }
    /**
     * 字符串转列表
     *
     * @param jsonStr json字符串
     * @param clazz   类型
     * @param <T>
     * @return
     * @throws JsonProcessingException
     */
    public static <T> Collection<T> string2List(String jsonStr, Class<T> clazz) throws IOException {
        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(Collection.class, clazz);
        return (Collection<T>) objectMapper.readValue(jsonStr, javaType);
    }
    /**
     * 对象转字符串
     *
     * @param object 对象
     * @return
     * @throws JsonProcessingException
     */
    public static String object2String(Object object) {
        if (object == null) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.warn("对象转字符串", e);
        }
        return null;
    }
    public static String list2String(List object){
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
src/main/java/com/common/core/utils/JwtTokenUtil.java
New file
@@ -0,0 +1,167 @@
package com.common.core.utils;
import com.common.core.constant.GlobalConst;
import com.common.redis.util.RedisUtil;
import com.common.security.configure.AppDetails;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Slf4j
@Component
public class JwtTokenUtil {
    @Autowired
    private RedisUtil redisUtil;
    public static final String CLAIM_KEY_APPID = "appid";
    public static final String CLAIM_KEY_USERID = "userid";
    private static final String CLAIM_KEY_CREATED = "created";
    private Long expiration = GlobalConst.TOKEN_EXPIRE+GlobalConst.TOKEN_EXPIRE_BUFF;
    /**
     * 根据负责生成JWT的token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, SM4Utils.getPassword())
                .compact();
    }
    /**
     * 从token中获取JWT中的负载
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = Jwts.parser()
                    .setSigningKey(SM4Utils.getPassword())
                    .parseClaimsJws(token)
                    .getBody();
        return claims;
    }
    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }
    /**
     * 从token中获取登录用户名
     */
    public String getAppidFromToken(String token) {
        String appid;
        try {
            Claims claims = getClaimsFromToken(token);
            appid = claims.get(CLAIM_KEY_APPID).toString();
        } catch (Exception e) {
            log.error("getAppidFromToken error:",e);
            appid = null;
        }
        return appid;
    }
    /**
     * 从token中获取登录用户名
     */
    public String getUseridFromToken(String token) {
        String userid;
        try {
            Claims claims = getClaimsFromToken(token);
            userid = claims.get(CLAIM_KEY_USERID)==null? "" : claims.get(CLAIM_KEY_USERID).toString();
        } catch (Exception e) {
            log.error("getUseridFromToken error:",e);
            userid = null;
        }
        return userid;
    }
    public Claims getClaim(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            return claims;
        } catch (Exception e) {
            log.error("getClaim error:",e);
            return null;
        }
    }
    /**
     * 验证token是否还有效
     *
     * @param token       客户端传入的token
     * @param userDetails 从数据库中查询出来的用户信息
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        String key= GlobalConst.TOKEN_KEY+userDetails.getUsername();
        String tmpKey= GlobalConst.TOKEN_KEY_TMP+userDetails.getUsername();
        Object useridobject = getClaim(token).get(JwtTokenUtil.CLAIM_KEY_USERID);
        if(!Objects.isNull(useridobject)){
            key +=":"+useridobject.toString();
            tmpKey +=":"+useridobject.toString();
        }
        return (redisUtil.get(key) !=null && token.equals(redisUtil.get(key).toString()) && !isTokenExpired(token))
                ||(redisUtil.get(tmpKey) !=null && token.equals(redisUtil.get(tmpKey).toString()) && !isTokenExpired(token));
    }
    /**
     * 判断token是否已经失效
     */
    private boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }
    /**
     * 从token中获取过期时间
     */
    private Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }
    /**
     * 根据用户信息生成token
     */
    public String generateToken(AppDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_APPID, userDetails.getUsername());
        if(StringUtils.isNotEmpty(userDetails.getUserid())){
            claims.put(CLAIM_KEY_USERID, userDetails.getUserid());
        }
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }
    /**
     * 判断token是否可以被刷新
     */
    public boolean canRefresh(String token) {
        return !isTokenExpired(token);
    }
    /**
     * 刷新token
     */
    public String refreshToken(String token) {
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }
}
src/main/java/com/common/core/utils/KitClassUtils.java
New file
@@ -0,0 +1,13 @@
package com.common.core.utils;
import java.util.ArrayList;
import java.util.List;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
public class KitClassUtils {
    public static List<Class<?>> tableclass = new ArrayList<>();
}
src/main/java/com/common/core/utils/ModelUtils.java
New file
@@ -0,0 +1,120 @@
package com.common.core.utils;
import com.alibaba.fastjson.JSONObject;
import com.common.core.constant.Constants;
import com.common.core.domain.BaseModel;
import com.common.security.utils.SecurityUtils;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.TableMapping;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.*;
import java.util.Date;
import java.util.Map;
import java.util.Set;
/**
 * @Author: 廖振钦
 * @description: ModelUtils
 * @date 2022-01-03:10:36
 * */
@Slf4j
public class ModelUtils {
    public static <T extends BaseModel> T MapToModel(Map<String, Object> map, T t){
        for(String key : map.keySet()){
            t.set(key, map.get(key));
        }
        return t;
    }
    public static <T extends BaseModel> T ObjectToModel(Object object, T t) {
        Field[] declaredFields = object.getClass().getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {
            String name = declaredFields[i].getName();
            Field f = declaredFields[i];
            f.setAccessible(true);
            try {
                t.set(name, f.get(object));
            } catch (IllegalAccessException e) {
                log.error(e.getMessage(), e);
            }
        }
        return t;
    }
    public static <T extends BaseModel> T JsonToModel(JSONObject json, T t) {
        for(String key : json.keySet()){
            if(json.get(key)==null){
                continue;
            }
            if(key.equalsIgnoreCase("dataid")){
                t.set("id", json.get(key));
            }else if(isPrimitive(json.get(key).getClass())){
                if(recordAttrHas(BeanHelper.propertyToColumn(key),t.getClass())){
                    t.set(BeanHelper.propertyToColumn(key), json.get(key));
                }else if(recordAttrHas(key,t.getClass())){
                    t.set(key, json.get(key));
                }
            }else {
                if(recordAttrHas(BeanHelper.propertyToColumn(key),t.getClass())){
                    t.set(BeanHelper.propertyToColumn(key), JSONObject.toJSONString(json.get(key)));
                }else if(recordAttrHas(key,t.getClass())){
                    t.set(key, JSONObject.toJSONString(json.get(key)));
                }
            }
        }
        return t;
    }
    private static Boolean recordAttrHas(String columnName,Class<? extends Model> clz){
        Set<String> names = TableMapping.me().getTable(clz).getColumnNameSet();
        if(names.contains(columnName)){
            return true;
        }
        return false;
    }
    /**
     * 基本数据类型和String类型判断
     *
     * @param clz
     * @return
     */
    public static boolean isPrimitive(Class<?> clz) {
        try {
            if (String.class.isAssignableFrom(clz) || Date.class.isAssignableFrom(clz)|| BigDecimal.class.isAssignableFrom(clz) || LocalDateTime.class.isAssignableFrom(clz)
                    || LocalDate.class.isAssignableFrom(clz)  || LocalTime.class.isAssignableFrom(clz)  || Year.class.isAssignableFrom(clz)
                    || YearMonth.class.isAssignableFrom(clz)  || Month.class.isAssignableFrom(clz) || clz.isPrimitive()) {
                return true;
            } else {
                return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
            }
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * 设置公共信息
     * @param baseModel
     * @param action
     */
    public static void generateCommonInfo(BaseModel baseModel,String action){
        String appid = SecurityUtils.byId();
        if (action.equals(Constants.ACTION_CREATE)) {
            baseModel.setCreateBy(appid);
            baseModel.setCreateTime(DateUtils.now());
            baseModel.setUpdateTime(DateUtils.now());
            baseModel.setUpdateBy(appid);
            baseModel.setIsDelete(0);
        }else if(action.equals(Constants.ACTION_UPDATE)){
            baseModel.setUpdateTime(DateUtils.now());
            baseModel.setUpdateBy(appid);
        }else {
            baseModel.setUpdateTime(new Date());
            baseModel.setUpdateBy(appid);
        }
    }
}
src/main/java/com/common/core/utils/PdfConverUtils.java
New file
@@ -0,0 +1,50 @@
package com.common.core.utils;
/**
 * @author 廖振钦
 * @date 2022-01-13 11:31
 */
public class PdfConverUtils {
    /**
     * @param
     *     wordPath
     * @param
     *     pdfPath
     * */
    public static void wordToPdf(String wordPath, String pdfPath){
    }
    /**
     * @param
     *     htmlPath
     * @param
     *     pdfPath
     * */
    public static void htmlToPdf(String htmlPath, String pdfPath){
    }
    /**
     * @param
     *     textPath
     * @param
     *     pdfPath
     * */
    public static void textToPdf(String textPath, String pdfPath){
    }
    /**
     * @param
     *     imgDirPath
     * @param
     *     pdfPath
     * */
    public static void imageToPdf(String imgDirPath, String pdfPath){
    }
}
src/main/java/com/common/core/utils/RegexUtils.java
New file
@@ -0,0 +1,28 @@
package com.common.core.utils;
import java.util.regex.Pattern;
/**
 * @Author holfeng
 * @Date 10:18 28/03/2022
 * @Version 1.0
 **/
public class RegexUtils {
    /**
     * 判断是否是手机号
     * @param tel 手机号
     * @return boolean true:是  false:否
     */
    public static boolean isMobile(String tel) {
        Pattern p = Pattern.compile("((\\+86|0086)?\\s*)((134[0-8]\\d{7})|(((13([0-3]|[5-9]))|(14[5-9])|15([0-3]|[5-9])|(16(2|[5-7]))|17([0-3]|[5-8])|18[0-9]|19(1|[8-9]))\\d{8})|(14(0|1|4)0\\d{7})|(1740([0-5]|[6-9]|[10-12])\\d{7}))");
        if(p.matcher(tel).matches()){
            return true;
        }
        return false;
    }
}
src/main/java/com/common/core/utils/RestUtil.java
New file
@@ -0,0 +1,310 @@
package com.common.core.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
@Slf4j
public class RestUtil {
    private static String domain = null;
    public static String getString(String s, String defval) {
        if (isEmpty(s)) {
            return (defval);
        }
        return (s.trim());
    }
    public static boolean isEmpty(Object object) {
        if (object == null) {
            return (true);
        }
        if ("".equals(object)) {
            return (true);
        }
        if ("null".equals(object)) {
            return (true);
        }
        return (false);
    }
    /**
     * RestAPI 调用器
     */
    private final static RestTemplate RT;
    static {
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setConnectTimeout(1000*60*5);
        requestFactory.setReadTimeout(1000*60*5);
        RT = new RestTemplate(requestFactory);
        // 解决乱码问题
        RT.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
    }
    public static RestTemplate getRestTemplate() {
        return RT;
    }
    /**
     * 发送 get 请求
     */
    public static JSONObject get(String url) {
        return getNative(url, null, null).getBody();
    }
    /**
     * 发送 get 请求
     */
    public static JSONObject get(String url, JSONObject variables) {
        return getNative(url, variables, null).getBody();
    }
    /**
     * 发送 get 请求
     */
    public static JSONObject get(String url, JSONObject variables, JSONObject params) {
        return getNative(url, variables, params).getBody();
    }
    /**
     * 发送 get 请求,返回原生 ResponseEntity 对象
     */
    public static ResponseEntity<JSONObject> getNative(String url, JSONObject variables, JSONObject params) {
        return request(url, HttpMethod.GET, variables, params);
    }
    public static ResponseEntity<JSONObject> getNative(String url, HttpHeaders headers) {
        return request(url, headers, HttpMethod.GET);
    }
    public static JSONObject get(String url, HttpHeaders headers) {
        return request(url, headers, HttpMethod.GET).getBody();
    }
    /**
     * 发送 Post 请求
     */
    public static JSONObject post(String url) {
        return postNative(url, null, new JSONObject()).getBody();
    }
    /**
     * 发送 Post 请求
     */
    public static JSONObject post(String url, JSONObject params) {
        return postNative(url, null, params).getBody();
    }
    /**
     * 发送 Post 请求
     */
    public static JSONObject post(String url, JSONObject variables, JSONObject params) {
        return postNative(url, variables, params).getBody();
    }
    public static JSONObject post(String url, HttpHeaders headers, JSONObject variables, Object params) {
        return request(url, headers, HttpMethod.POST, variables, params).getBody();
    }
    /**
     * 发送 POST 请求,返回原生 ResponseEntity 对象
     */
    public static ResponseEntity<JSONObject> postNative(String url, HttpHeaders headers, JSONObject variables, Object params) {
        return request(url, headers, HttpMethod.POST, variables, params);
    }
    public static ResponseEntity<JSONObject> postNative(String url, HttpHeaders headers, JSONObject variables, String params) {
        return request(url, headers, HttpMethod.POST, variables, params);
    }
    public static ResponseEntity<Object> postNativeObject(String url, HttpHeaders headers, JSONObject variables, String params) {
        return requestObject(url, headers, HttpMethod.POST, variables, params);
    }
    public static ResponseEntity<JSONObject> postNative(String url, JSONObject variables, JSONObject params) {
        return request(url, HttpMethod.POST, variables, params);
    }
    public static ResponseEntity<JSONObject> postNative(String url, JSONObject variables, JSONArray params) {
        return request(url, HttpMethod.POST, variables, params);
    }
    /**
     * 发送 PATCH 请求,返回原生 ResponseEntity 对象
     */
    public static ResponseEntity<JSONObject> patchNative(String url, HttpHeaders headers, JSONObject variables, String params) {
        return request(url, headers, HttpMethod.PATCH, variables, params);
    }
    /**
     * 发送 put 请求
     */
    public static JSONObject put(String url) {
        return putNative(url, null, null).getBody();
    }
    /**
     * 发送 put 请求
     */
    public static JSONObject put(String url, JSONObject params) {
        return putNative(url, null, params).getBody();
    }
    /**
     * 发送 put 请求
     */
    public static JSONObject put(String url, JSONObject variables, JSONObject params) {
        return putNative(url, variables, params).getBody();
    }
    /**
     * 发送 put 请求,返回原生 ResponseEntity 对象
     */
    public static ResponseEntity<JSONObject> putNative(String url, JSONObject variables, JSONObject params) {
        return request(url, HttpMethod.PUT, variables, params);
    }
    /**
     * 发送 delete 请求
     */
    public static JSONObject delete(String url) {
        return deleteNative(url, null, null).getBody();
    }
    /**
     * 发送 delete 请求
     */
    public static JSONObject delete(String url, JSONObject variables, JSONObject params) {
        return deleteNative(url, variables, params).getBody();
    }
    /**
     * 发送 delete 请求,返回原生 ResponseEntity 对象
     */
    public static ResponseEntity<JSONObject> deleteNative(String url, JSONObject variables, JSONObject params) {
        return request(url, HttpMethod.DELETE, null, variables, params, JSONObject.class);
    }
    /**
     * 发送请求
     */
    public static ResponseEntity<JSONObject> request(String url, HttpMethod method, JSONObject variables, JSONObject params) {
        return request(url, method, getHeaderApplicationJson(), variables, params, JSONObject.class);
    }
    public static ResponseEntity<JSONObject> request(String url, HttpMethod method, JSONObject variables, JSONArray params) {
        return request(url, method, getHeaderApplicationJson(), variables, params, JSONObject.class);
    }
    public static ResponseEntity<JSONObject> request(String url, HttpHeaders headers, HttpMethod method, JSONObject variables, Object params) {
        return request(url, method, headers, variables, params, JSONObject.class);
    }
    public static ResponseEntity<Object> requestObject(String url, HttpHeaders headers, HttpMethod method, JSONObject variables, Object params) {
        return request(url, method, headers, variables, params, Object.class);
    }
    public static ResponseEntity<JSONObject> request(String url, HttpHeaders headers,HttpMethod method) {
        return request(url, method, headers, null, null, JSONObject.class);
    }
    /**
     * 发送请求
     *
     * @param url          请求地址
     * @param method       请求方式
     * @param headers      请求头  可空
     * @param variables    请求url参数 可空
     * @param params       请求body参数 可空
     * @param responseType 返回类型
     * @return ResponseEntity<responseType>
     */
    public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers, JSONObject variables, Object params, Class<T> responseType) {
        log.info(" RestUtil  --- request ---  url = "+ url);
        if (StringUtils.isEmpty(url)) {
            throw new RuntimeException("url 不能为空");
        }
        if (method == null) {
            throw new RuntimeException("method 不能为空");
        }
        if (headers == null) {
            headers = new HttpHeaders();
        }
        // 请求体
        String body = "";
        if (params != null) {
            log.info(url+",json="+params.toString());
            if (params instanceof JSONObject) {
                body = ((JSONObject) params).toJSONString();
            } else {
                body = params.toString();
            }
        }
        // 拼接 url 参数
        if (variables != null) {
            log.info(url+",variables:"+asUrlVariables(variables));
            url += ("?" + asUrlVariables(variables));
        }
        // 发送请求
        HttpEntity<String> request = new HttpEntity<>(body, headers);
        ResponseEntity<T> resp = RT.exchange(url, method, request, responseType);
        Optional o = Optional.ofNullable(resp.getBody());
        if(o.isPresent()) {
            log.info(url + ": reponsebody=" + o.get());
        }
        return resp;
    }
    /**
     * 获取JSON请求头
     */
    public static HttpHeaders getHeaderApplicationJson() {
        return getHeader(MediaType.APPLICATION_JSON_UTF8_VALUE);
    }
    /**
     * 获取请求头
     */
    public static HttpHeaders getHeader(String mediaType) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType(mediaType));
        headers.add("Accept", mediaType);
        return headers;
    }
    /**
     * 将 JSONObject 转为 a=1&b=2&c=3...&n=n 的形式
     */
    public static String asUrlVariables(JSONObject variables) {
        Map<String, Object> source = variables.getInnerMap();
        Iterator<String> it = source.keySet().iterator();
        StringBuilder urlVariables = new StringBuilder();
        while (it.hasNext()) {
            String key = it.next();
            String value = "";
            Object object = source.get(key);
            if (object != null) {
                if (!StringUtils.isEmpty(object.toString())) {
                    value = object.toString();
                }
            }
            urlVariables.append("&").append(key).append("=").append(value);
        }
        // 去掉第一个&
        return urlVariables.substring(1);
    }
}
src/main/java/com/common/core/utils/SM4Utils.java
New file
@@ -0,0 +1,53 @@
package com.common.core.utils;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import org.springframework.core.env.Environment;
/**
 * @author 廖振钦
 * @date 20/12/2021
 */
public class SM4Utils {
    private static String password = "";
    public static String getPassword() {
        getInit();
        return password;
    }
    public static void getInit(){
        if(StringUtils.isBlank(password)) {
            SecretsManagerUtils secretsManagerUtils = SpringContextUtils.getBean(SecretsManagerUtils.class);
            Environment env = SpringContextUtils.getBean(Environment.class);
            password = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.sm4")).getString("SM4Secret");
        }
    }
    public static String decrypt(String text){
        getInit();
        if(text.startsWith("DES@")) {
            SymmetricCrypto sm4 = SmUtil.sm4(password.getBytes());
            return sm4.decryptStr(text);
        }
        return text;
    }
    public static String decryptStr(String text){
        if(null == text || "".equals(text)){
            return text;
        }
        getInit();
        SymmetricCrypto sm4 = SmUtil.sm4(password.getBytes());
        return sm4.decryptStr(text);
    }
    public static String encryptStr(String text){
        if(null == text || "".equals(text)){
            return text;
        }
        getInit();
        SymmetricCrypto sm4 = SmUtil.sm4(password.getBytes());
        return sm4.encryptHex(text);
    }
}
src/main/java/com/common/core/utils/SecretsManagerUtils.java
New file
@@ -0,0 +1,105 @@
package com.common.core.utils;
import com.alibaba.fastjson.JSONObject;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.secretsmanager.model.*;
import java.util.Base64;
@Slf4j
@Component
public class SecretsManagerUtils {
    @Value("${aws.region}")
    private String regionStr;
    @Value("${aws.secrets.systemauth}")
    private String systemauthName;
    @Value("${aws.secrets.mysql}")
    private String mysqlsecretName;
    @Value("${aws.secrets.sm4}")
    private String SM4SecretName;
    @Value("${aws.secrets.mailauth}")
    private String mailauthName;
    private static JSONObject systemAuth;
    private static JSONObject mysqlsecret;
    private static JSONObject SM4Secret;
    private static JSONObject mailauth;
    private void getInit() {
        if(systemAuth==null){
            systemAuth=this.getSecretByName(systemauthName);
        }
        if(mysqlsecret==null){
            mysqlsecret=this.getSecretByName(mysqlsecretName);
        }
        if(SM4Secret==null){
            SM4Secret=this.getSecretByName(SM4SecretName);
        }
        if(mailauth==null){
            mailauth=this.getSecretByName(mailauthName);
        }
    }
    public JSONObject getSecretByName(String secretName) {
        String key_id = System.getenv("AWS_ACCESS_KEY_ID");
        String access_key = System.getenv("AWS_SECRET_ACCESS_KEY");
        Region region = Region.of(regionStr);
        SecretsManagerClient client = null;
        if(StringUtils.isNotEmpty(key_id) && StringUtils.isNotEmpty(access_key)){
            AwsBasicCredentials awsCreds = AwsBasicCredentials.create(key_id,access_key);
            client = SecretsManagerClient.builder().credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                    .region(region)
                    .build();
        }else {
            AwsCredentialsProvider credentialsProvider = InstanceProfileCredentialsProvider.builder().build();
            client = SecretsManagerClient.builder().credentialsProvider(credentialsProvider)
                    .region(region)
                    .build();
        }
        String secret, decodedBinarySecret;
        GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
                .secretId(secretName)
                .build();
        GetSecretValueResponse getSecretValueResponse = null;
        try {
            getSecretValueResponse = client.getSecretValue(getSecretValueRequest);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BizException(ResultCodeEnum.AWS_RT_ERROR);
        }
        if (getSecretValueResponse.secretString() != null) {
            return JSONObject.parseObject(getSecretValueResponse.secretString());
        }
        else {
            decodedBinarySecret = new String(Base64.getDecoder().decode(getSecretValueResponse.secretBinary().asByteBuffer()).array());
            return JSONObject.parseObject(getSecretValueResponse.secretString());
        }
    }
    public JSONObject getSecret(String secretName) {
        this.getInit();
        if(secretName.equals(systemauthName)){
            return systemAuth;
        }
        if(secretName.equals(mysqlsecretName)){
            return mysqlsecret;
        }
        if(secretName.equals(SM4SecretName)){
            return SM4Secret;
        }
        if(secretName.equals(mailauthName)){
            return mailauth;
        }
        return getSecretByName(secretName);
    }
}
src/main/java/com/common/core/utils/SignUtil.java
New file
@@ -0,0 +1,73 @@
package com.common.core.utils;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
public class SignUtil {
    private static final String DEFAULT_SECRET = "1qaz@WSX#$%&";
    public static String sign(String body, Map<String, String[]> params, String[] paths) {
        StringBuilder sb = new StringBuilder();
        if (StringUtils.isNotBlank(body)) {
            sb.append(body).append('#');
        }
        if (!CollectionUtils.isEmpty(params)) {
            params.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(paramEntry -> {
                String paramValue = String.join(",", Arrays.stream(paramEntry.getValue()).sorted().toArray(String[]::new));
                sb.append(paramEntry.getKey()).append("=").append(paramValue).append('#');
            });
        }
        if (ArrayUtils.isNotEmpty(paths)) {
            String pathValues = String.join(",", Arrays.stream(paths).sorted().toArray(String[]::new));
            sb.append(pathValues);
        }
        String createSign = HmacUtils.hmacSha256Hex(DEFAULT_SECRET, sb.toString());
        return createSign;
    }
    public static String sign(String secret, String body, Map<String, String[]> params, String[] paths) {
        StringBuilder sb = new StringBuilder();
        if (StringUtils.isNotBlank(body)) {
            sb.append(body).append('#');
        }
        if (!CollectionUtils.isEmpty(params)) {
            params.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(paramEntry -> {
                String paramValue = String.join(",", Arrays.stream(paramEntry.getValue()).sorted().toArray(String[]::new));
                sb.append(paramEntry.getKey()).append("=").append(paramValue).append('#');
            });
        }
        if (ArrayUtils.isNotEmpty(paths)) {
            String pathValues = String.join(",", Arrays.stream(paths).sorted().toArray(String[]::new));
            sb.append(pathValues);
        }
        String createSign = HmacUtils.hmacSha256Hex(secret, sb.toString());
        return createSign;
    }
    public static void main(String[] args) {
        String body = "{\n" + "\t\"name\": \"hjzgg\",\n" + "\t\"age\": 26\n" + "}";
        Map<String, String[]> params = new HashMap<>();
        params.put("var3", new String[]{"3"});
        params.put("var4", new String[]{"4"});
        String[] paths = new String[]{"1", "2"};
        System.out.println(sign(body, params, paths));
    }
}
src/main/java/com/common/core/utils/SpringContextUtils.java
New file
@@ -0,0 +1,83 @@
package com.common.core.utils;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Component
public class SpringContextUtils implements ApplicationContextAware {
    /**
     * 上下文对象实例
     */
    private static ApplicationContext applicationContext;
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }
    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
    /**
     * 获取HttpServletRequest
     */
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }
    public static String getOrigin(){
        HttpServletRequest request = getHttpServletRequest();
        return request.getHeader("Origin");
    }
    /**
     * 通过name获取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }
    /**
     * 通过class获取Bean.
     *
     * @param clazz
     * @param       <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }
    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param       <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }
    public static String getEnvParam(String key){
        Environment env = getBean(Environment.class);
        return env.getProperty(key);
    }
}
src/main/java/com/common/core/utils/StringUtils.java
New file
@@ -0,0 +1,293 @@
package com.common.core.utils;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author : zhouxb
 * date: 2017-10-25 23:54
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    /**
     * SQL格式化表名和字段名加"`"
     *
     * @param fieldName
     * @return
     */
    public static String formatFieldNameColumn(String fieldName) {
        return new StringBuffer("`").append(fieldName).append("`").toString();
    }
    public static String formatStringFiledVal(String fieldName) {
        return new StringBuffer("'").append(fieldName).append("'").toString();
    }
    public static String formatFieldNameColumn4Rdb(String fieldName) {
        return new StringBuffer("\"").append(fieldName).append("\"").toString();
    }
    /**
     * 去掉字符串指定前缀部分
     *
     * @param source 字符串
     * @param prefix 前缀
     */
    public static String cutPrefix(String source, String prefix) {
        if (isNotBlank(source)) {
            if (isNotEmpty(prefix)) {
                if (source.startsWith(prefix)) {
                    int prefixLen = prefix.length();
                    source = source.substring(prefixLen);
                }
            }
        }
        return source;
    }
    /**
     * 去掉字符串指定后缀部分
     *
     * @param source 字符串
     * @param suffix 后缀
     */
    public static String cutSuffix(String source, String suffix) {
        if (isNotBlank(source)) {
            if (isNotEmpty(suffix)) {
                int suffixLen = suffix.length();
                source = source.substring(0, source.length() - suffixLen);
            }
        }
        return source;
    }
    /**
     * 去掉字符串中指定字符及其后面部分
     *
     * @param source    字符串
     * @param separator 指定字符
     */
    public static String cutLaterOfCharacter(String source, String separator) {
        if (isNotBlank(source)) {
            if (isNotEmpty(separator)) {
                int index = source.indexOf(separator);
                if (index > -1) {
                    source = source.substring(0, index);
                }
            }
        }
        return source;
    }
    /**
     * sql语句like部分
     * 增加对%转义
     *
     * @param in
     * @return
     */
    public static String sqlLike(String in) {
//        if (StringUtils.isNotEmpty(in) && in.contains("%")) {
//            in = in.replace("\\", "\\\\");
//            in = in.replace("%", "\\%");
//            in = in.replace("_", "\\_");
//        }
        return "%" + sqlLikeXML(in) + "%";
    }
    public static String sqlLikeXML(String in) {
        if (StringUtils.isNotEmpty(in) && (in.contains("%") || in.contains("_") || in.contains("\\"))) {
            in = in.replace("\\", "\\\\");
            in = in.replace("%", "\\%");
            in = in.replace("_", "\\_");
        }
        return in;
    }
    /**
     * 按指定分割符分割字符串,返回结果中不包含空字符串
     */
    public static String[] tokenizeToStringArray(String cs, String delimiters) {
        String[] csAry = org.springframework.util.StringUtils.tokenizeToStringArray(cs, delimiters);
        List<String> result = new ArrayList<>(csAry.length);
        for (String csa : csAry) {
            if (isNotBlank(csa)) {
                result.add(csa);
            }
        }
        return result.toArray(new String[result.size()]);
    }
    /**
     * 拆分token方法
     *
     * @param cs
     * @return
     */
    public static String[] tokenizeToStringArray(String cs) {
        return tokenizeToStringArray(cs, ",");
    }
    /**
     * 将集合用转换为指定分隔符连接的字符串
     *
     * @param coll
     * @param delimiter
     * @return
     */
    public static String collectionToDelimitedString(Collection<?> coll, String delimiter) {
        return org.springframework.util.StringUtils.collectionToDelimitedString(coll, delimiter);
    }
    /**
     * 将集合用","连接的字符串
     *
     * @param coll
     * @return
     */
    public static String collectionToDelimitedString(Collection<?> coll) {
        return collectionToDelimitedString(coll, ",");
    }
    /**
     * 将数组转换为用","连接的字符串
     *
     * @param ary
     * @return
     */
//    public static String arrayToCommaDelimitedString(Object[] ary) {
//        return arrayToDelimitedString(ary, ",");
//    }
//
//    /**
//     * 将数组转换为用指定分隔符连接的字符串
//     *
//     * @param ary
//     * @param delimiter
//     * @return
//     */
//    public static String arrayToDelimitedString(Object[] ary, String delimiter) {
//        if (ObjectUtils.isEmpty(ary)) {
//            return "";
//        }
//        if (ary.length == 1) {
//            return ObjectUtils.nullSafeToString(ary[0]);
//        }
//
//        StringBuilder sb = new StringBuilder();
//        for (int i = 0; i < ary.length; i++) {
//            if (i > 0) {
//                sb.append(delimiter);
//            }
//            sb.append(ary[i]);
//        }
//        return sb.toString();
//    }
    /**
     * 根据字段类型返回字段作为where条件查询判断字段时使用的分隔符
     *
     * @param type
     * @return
     */
    public static String getSeparator(String type) {
        if ("string".equalsIgnoreCase(type)) {
            return "'";
        } else {
            return "";
        }
    }
    /**
     * 返回隐藏信息的String
     *
     * @param input
     * @return
     */
    public static String getConcealStr(String input) {
        String ss = "*";
        String ds = "***";
        if (StringUtils.isNotBlank(input)) {
            if (input.length() <= 2) {
                int i = input.length() / 2;
                if (i == 0) {
                    return ss;
                } else {
                    return input.substring(0, i) + ss;
                }
            } else {
                int i = input.length() / 3;
                return input.substring(0, i) + ds + input.substring(input.length() - i);
            }
        }
        return null;
    }
    /**
     * 数据脱敏方法
     *
     * @param input
     * @return
     */
    public static String concealString(String input) {
        if (StringUtils.isEmpty(input)) {
            return input;
        }
        return StringUtils.collectionToDelimitedString(Arrays.stream(input.split(",")).map(e -> concealStringSingle(e)).collect(Collectors.toList()));
    }
    /**
     * 单个字符串数据脱敏方法
     *
     * @param input
     * @return
     */
    public static String concealStringSingle(String input) {
        if (input.startsWith("Untagged") || input.startsWith("Other tag")) {
            return input;
        }
        if (1 == input.length()) {
            return "*";
        }
        int i = (int) Math.ceil((double) input.length() / 3);
        int j = (int) Math.floor((double) input.length() / 3);
        StringBuffer sbf = new StringBuffer(input.substring(0, i));
        for (int x = 0; x < input.length() - i - j; x++) {
            sbf.append("*");
        }
        return sbf.append(input.substring(input.length() - j)).toString();
    }
//    public static String formatList2SqlInString(List<String> propertyList) {
//        if (CollectionUtils.isNotEmpty(propertyList)) {
//            return propertyList.stream().map(e -> new StringBuffer().append("'").append(e).append("'").toString()).collect(Collectors.joining(CommonSqlConst.F_COMMA));
//        }
//        return null;
//    }
//    public static String formatSqlFieldNotBlank(String fieldName) {
//        if (StringUtils.isNotBlank(fieldName)) {
//            return new StringBuffer().append(fieldName).append(" is not null ").append(CommonSqlConst.SQL_AND).append(fieldName).append(" !='' ").toString();
//        }
//        return null;
//    }
    public static String formatSqlCondition(String sql) {
        sql = sql.replace("&&", "and").replace("||", "or").replace("`", "");
        return sql;
    }
    public static String formatByMap(String content, Map<String, String> varValueMap) {
        for(Map.Entry<String,String> entry:varValueMap.entrySet()){
            content=content.replace("{"+entry.getKey()+"}",entry.getValue());
        }
        return content;
    }
}
src/main/java/com/common/core/utils/TemplateUtils.groovy
New file
@@ -0,0 +1,48 @@
package com.common.core.utils
import com.jfinal.plugin.activerecord.Db
import com.jfinal.plugin.activerecord.Record
import java.util.stream.Collectors
/**
 *
 * @author 廖振钦
 * @date 2021-02-09
 * @company deloitte
 * */
class TemplateUtils {
    def static generateTemp(def text, def params){
        def template = new groovy.text.StreamingTemplateEngine().createTemplate(text)
        return template.make(params).toString()
    }
    def static ExecuteSqlToList(def sql, def params){
        List<Record> recordList= Db.find(TemplateUtils.generateTemp(sql, params))
        def list = recordList.stream().map({ record -> record.getColumns() }).collect(Collectors.toList())
        return list
    }
    def static ExecuteSqlToMapEntity(def sql, def params){
        Record record= Db.findFirst(TemplateUtils.generateTemp(sql, params))
        if(null == record){
            return null
        }else {
            def map = record.getColumns();
            return map
        }
    }
    def static ExecuteSqlToEntity(def sql, def params){
        Record record= Db.findFirst(TemplateUtils.generateTemp(sql, params))
        if(null == record){
            return null
        }else {
            def map = record.getColumns().values();
            if (map.size() == 1) {
                return map[0]
            }
            return map
        }
    }
}
src/main/java/com/common/core/utils/ThreadUtils.java
New file
@@ -0,0 +1,18 @@
package com.common.core.utils;
import lombok.SneakyThrows;
import java.lang.reflect.Method;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
public class ThreadUtils {
    public static ThreadPoolExecutor executor(){
        return SpringContextUtils.getBean(ThreadPoolExecutor.class);
    }
}
src/main/java/com/common/core/utils/UUID.java
New file
@@ -0,0 +1,484 @@
package com.common.core.utils;
import com.common.core.exception.BizException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
 * 提供通用唯一识别码(universally unique identifier)(UUID)实现
 *
 */
public final class UUID implements java.io.Serializable, Comparable<UUID>
{
    private static final long serialVersionUID = -1185015143654744140L;
    /**
     * SecureRandom 的单例
     *
     */
    private static class Holder
    {
        static final SecureRandom numberGenerator = getSecureRandom();
    }
    /** 此UUID的最高64有效位 */
    private final long mostSigBits;
    /** 此UUID的最低64有效位 */
    private final long leastSigBits;
    /**
     * 私有构造
     *
     * @param data 数据
     */
    private UUID(byte[] data)
    {
        long msb = 0;
        long lsb = 0;
        assert data.length == 16 : "data must be 16 bytes in length";
        for (int i = 0; i < 8; i++)
        {
            msb = (msb << 8) | (data[i] & 0xff);
        }
        for (int i = 8; i < 16; i++)
        {
            lsb = (lsb << 8) | (data[i] & 0xff);
        }
        this.mostSigBits = msb;
        this.leastSigBits = lsb;
    }
    /**
     * 使用指定的数据构造新的 UUID。
     *
     * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
     */
    public UUID(long mostSigBits, long leastSigBits)
    {
        this.mostSigBits = mostSigBits;
        this.leastSigBits = leastSigBits;
    }
    /**
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
     *
     * @return 随机生成的 {@code UUID}
     */
    public static UUID fastUUID()
    {
        return randomUUID(false);
    }
    /**
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
     *
     * @return 随机生成的 {@code UUID}
     */
    public static UUID randomUUID()
    {
        return randomUUID(true);
    }
    /**
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
     *
     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
     * @return 随机生成的 {@code UUID}
     */
    public static UUID randomUUID(boolean isSecure)
    {
        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
        byte[] randomBytes = new byte[16];
        ng.nextBytes(randomBytes);
        randomBytes[6] &= 0x0f; /* clear version */
        randomBytes[6] |= 0x40; /* set to version 4 */
        randomBytes[8] &= 0x3f; /* clear variant */
        randomBytes[8] |= 0x80; /* set to IETF variant */
        return new UUID(randomBytes);
    }
    /**
     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
     *
     * @param name 用于构造 UUID 的字节数组。
     *
     * @return 根据指定数组生成的 {@code UUID}
     */
    public static UUID nameUUIDFromBytes(byte[] name)
    {
        MessageDigest md;
        try
        {
            md = MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException nsae)
        {
            throw new InternalError("MD5 not supported");
        }
        byte[] md5Bytes = md.digest(name);
        md5Bytes[6] &= 0x0f; /* clear version */
        md5Bytes[6] |= 0x30; /* set to version 3 */
        md5Bytes[8] &= 0x3f; /* clear variant */
        md5Bytes[8] |= 0x80; /* set to IETF variant */
        return new UUID(md5Bytes);
    }
    /**
     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
     *
     * @param name 指定 {@code UUID} 字符串
     * @return 具有指定值的 {@code UUID}
     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
     *
     */
    public static UUID fromString(String name)
    {
        String[] components = name.split("-");
        if (components.length != 5)
        {
            throw new IllegalArgumentException("Invalid UUID string: " + name);
        }
        for (int i = 0; i < 5; i++)
        {
            components[i] = "0x" + components[i];
        }
        long mostSigBits = Long.decode(components[0]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[1]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[2]).longValue();
        long leastSigBits = Long.decode(components[3]).longValue();
        leastSigBits <<= 48;
        leastSigBits |= Long.decode(components[4]).longValue();
        return new UUID(mostSigBits, leastSigBits);
    }
    /**
     * 返回此 UUID 的 128 位值中的最低有效 64 位。
     *
     * @return 此 UUID 的 128 位值中的最低有效 64 位。
     */
    public long getLeastSignificantBits()
    {
        return leastSigBits;
    }
    /**
     * 返回此 UUID 的 128 位值中的最高有效 64 位。
     *
     * @return 此 UUID 的 128 位值中最高有效 64 位。
     */
    public long getMostSignificantBits()
    {
        return mostSigBits;
    }
    /**
     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
     * <p>
     * 版本号具有以下含意:
     * <ul>
     * <li>1 基于时间的 UUID
     * <li>2 DCE 安全 UUID
     * <li>3 基于名称的 UUID
     * <li>4 随机生成的 UUID
     * </ul>
     *
     * @return 此 {@code UUID} 的版本号
     */
    public int version()
    {
        // Version is bits masked by 0x000000000000F000 in MS long
        return (int) ((mostSigBits >> 12) & 0x0f);
    }
    /**
     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
     * <p>
     * 变体号具有以下含意:
     * <ul>
     * <li>0 为 NCS 向后兼容保留
     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
     * <li>6 保留,微软向后兼容
     * <li>7 保留供以后定义使用
     * </ul>
     *
     * @return 此 {@code UUID} 相关联的变体号
     */
    public int variant()
    {
        // This field is composed of a varying number of bits.
        // 0 - - Reserved for NCS backward compatibility
        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
        // 1 1 0 Reserved, Microsoft backward compatibility
        // 1 1 1 Reserved for future definition.
        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
    }
    /**
     * 与此 UUID 相关联的时间戳值。
     *
     * <p>
     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
     *
     * <p>
     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
     *
     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
     */
    public long timestamp() throws UnsupportedOperationException
    {
        checkTimeBase();
        return (mostSigBits & 0x0FFFL) << 48//
                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
                | mostSigBits >>> 32;
    }
    /**
     * 与此 UUID 相关联的时钟序列值。
     *
     * <p>
     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
     * <p>
     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
     * UnsupportedOperationException。
     *
     * @return 此 {@code UUID} 的时钟序列
     *
     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
     */
    public int clockSequence() throws UnsupportedOperationException
    {
        checkTimeBase();
        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
    }
    /**
     * 与此 UUID 相关的节点值。
     *
     * <p>
     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
     * <p>
     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
     *
     * @return 此 {@code UUID} 的节点值
     *
     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
     */
    public long node() throws UnsupportedOperationException
    {
        checkTimeBase();
        return leastSigBits & 0x0000FFFFFFFFFFFFL;
    }
    /**
     * 返回此{@code UUID} 的字符串表现形式。
     *
     * <p>
     * UUID 的字符串表示形式由此 BNF 描述:
     *
     * <pre>
     * {@code
     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
     * time_low               = 4*<hexOctet>
     * time_mid               = 2*<hexOctet>
     * time_high_and_version  = 2*<hexOctet>
     * variant_and_sequence   = 2*<hexOctet>
     * node                   = 6*<hexOctet>
     * hexOctet               = <hexDigit><hexDigit>
     * hexDigit               = [0-9a-fA-F]
     * }
     * </pre>
     *
     * </blockquote>
     *
     * @return 此{@code UUID} 的字符串表现形式
     * @see #toString(boolean)
     */
    @Override
    public String toString()
    {
        return toString(false);
    }
    /**
     * 返回此{@code UUID} 的字符串表现形式。
     *
     * <p>
     * UUID 的字符串表示形式由此 BNF 描述:
     *
     * <pre>
     * {@code
     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
     * time_low               = 4*<hexOctet>
     * time_mid               = 2*<hexOctet>
     * time_high_and_version  = 2*<hexOctet>
     * variant_and_sequence   = 2*<hexOctet>
     * node                   = 6*<hexOctet>
     * hexOctet               = <hexDigit><hexDigit>
     * hexDigit               = [0-9a-fA-F]
     * }
     * </pre>
     *
     * </blockquote>
     *
     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
     * @return 此{@code UUID} 的字符串表现形式
     */
    public String toString(boolean isSimple)
    {
        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
        // time_low
        builder.append(digits(mostSigBits >> 32, 8));
        if (false == isSimple)
        {
            builder.append('-');
        }
        // time_mid
        builder.append(digits(mostSigBits >> 16, 4));
        if (false == isSimple)
        {
            builder.append('-');
        }
        // time_high_and_version
        builder.append(digits(mostSigBits, 4));
        if (false == isSimple)
        {
            builder.append('-');
        }
        // variant_and_sequence
        builder.append(digits(leastSigBits >> 48, 4));
        if (false == isSimple)
        {
            builder.append('-');
        }
        // node
        builder.append(digits(leastSigBits, 12));
        return builder.toString();
    }
    /**
     * 返回此 UUID 的哈希码。
     *
     * @return UUID 的哈希码值。
     */
    @Override
    public int hashCode()
    {
        long hilo = mostSigBits ^ leastSigBits;
        return ((int) (hilo >> 32)) ^ (int) hilo;
    }
    /**
     * 将此对象与指定对象比较。
     * <p>
     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
     *
     * @param obj 要与之比较的对象
     *
     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
     */
    @Override
    public boolean equals(Object obj)
    {
        if ((null == obj) || (obj.getClass() != UUID.class))
        {
            return false;
        }
        UUID id = (UUID) obj;
        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
    }
    // Comparison Operations
    /**
     * 将此 UUID 与指定的 UUID 比较。
     *
     * <p>
     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
     *
     * @param val 与此 UUID 比较的 UUID
     *
     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
     *
     */
    @Override
    public int compareTo(UUID val)
    {
        // The ordering is intentionally set up so that the UUIDs
        // can simply be numerically compared as two numbers
        return (this.mostSigBits < val.mostSigBits ? -1 : //
                (this.mostSigBits > val.mostSigBits ? 1 : //
                        (this.leastSigBits < val.leastSigBits ? -1 : //
                                (this.leastSigBits > val.leastSigBits ? 1 : //
                                        0))));
    }
    // -------------------------------------------------------------------------------------------------------------------
    // Private method start
    /**
     * 返回指定数字对应的hex值
     *
     * @param val 值
     * @param digits 位
     * @return 值
     */
    private static String digits(long val, int digits)
    {
        long hi = 1L << (digits * 4);
        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
    }
    /**
     * 检查是否为time-based版本UUID
     */
    private void checkTimeBase()
    {
        if (version() != 1)
        {
            throw new UnsupportedOperationException("Not a time-based UUID");
        }
    }
    /**
     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
     *
     * @return {@link SecureRandom}
     */
    public static SecureRandom getSecureRandom()
    {
        try
        {
            return SecureRandom.getInstance("SHA1PRNG");
        }
        catch (NoSuchAlgorithmException e)
        {
            throw new BizException(e.getMessage());
        }
    }
    /**
     * 获取随机数生成器对象<br>
     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
     *
     * @return {@link ThreadLocalRandom}
     */
    public static ThreadLocalRandom getRandom()
    {
        return ThreadLocalRandom.current();
    }
}
src/main/java/com/common/filter/CorsFilter.java
New file
@@ -0,0 +1,21 @@
package com.common.filter;
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;
public class CorsFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "*");
        response.addHeader("Access-Control-Allow-Headers", "content-type,pi-token");
        response.addHeader("Access-Control-Max-Age", "1800");//30 min
        filterChain.doFilter(request, response);
    }
}
src/main/java/com/common/filter/JwtAuthenticationTokenFilter.java
New file
@@ -0,0 +1,94 @@
package com.common.filter;
import com.common.annotation.NoToken;
import com.common.core.utils.JwtTokenUtil;
import com.common.core.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = ((RequestMappingHandlerMapping)SpringContextUtils.getBean("requestMappingHandlerMapping")).getHandlerMethods();
        Set<String> anonymousUrls = new HashSet<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethods.entrySet()) {
            // 获取所有接口的方法
            HandlerMethod handlerMethod = infoEntry.getValue();
            if ( handlerMethod.hasMethodAnnotation(NoToken.class)) {
                final PatternsRequestCondition requestCondition = infoEntry.getKey().getPatternsCondition();
                Optional.ofNullable(requestCondition).orElseThrow(RuntimeException::new);
                anonymousUrls.addAll(requestCondition.getPatterns().stream().map(m -> SpringContextUtils.getEnvParam("server.servlet.context-path")+m).collect(Collectors.toList()));
            }
        }
        String uri = request.getRequestURI();
        boolean visit = true;
        for(String item : Arrays.asList(new String[]{
                ".html",
                ".css",
                ".js",
                ".png",
                ".ttf",
                ".ico",
                ".woff",
                "/swagger-resources",
                "/v2/api-docs",
                "/druid"
        })){
            if(uri.contains(item)){
                visit = false;
            }
        }
        if(!anonymousUrls.contains(uri) && !HttpMethod.OPTIONS.matches(request.getMethod()) && visit) {
            String token = request.getHeader("pi-token");
            log.info("TOKEN:"+token);
            if(StringUtils.isEmpty(token)){
                throw new AuthenticationServiceException("TOKEN为空");
            }
            String appid = jwtTokenUtil.getAppidFromToken(token);
            if (appid != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(appid);
                if (jwtTokenUtil.validateToken(token, userDetails)) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }else{
                    throw new AuthenticationServiceException("TOKEN无效");
                }
            }
        }
        chain.doFilter(request,response);
    }
}
src/main/java/com/common/filter/RequestCachingFilter.java
New file
@@ -0,0 +1,78 @@
package com.common.filter;
import com.alibaba.fastjson.JSONObject;
import com.common.sign.BufferedHttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
public class RequestCachingFilter extends OncePerRequestFilter {
    private static Logger LOGGER = LoggerFactory.getLogger(RequestCachingFilter.class);
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        boolean isFirstRequest = !isAsyncDispatch(request);
        HttpServletRequest requestToUse = request;
        if (isFirstRequest && !(request instanceof BufferedHttpServletRequest)) {
            requestToUse = new BufferedHttpServletRequest(request, 1024);
        }
        try {
            filterChain.doFilter(requestToUse, response);
        } catch (Exception e) {
            LOGGER.error("RequestCachingFilter>>>>>>>>>", e);
        } finally {
//            this.printRequest(requestToUse); TODO
            if (requestToUse instanceof BufferedHttpServletRequest) {
                ((BufferedHttpServletRequest) requestToUse).release();
            }
        }
    }
    private void printRequest(HttpServletRequest request) {
        String body = StringUtils.EMPTY;
        try {
            if (request instanceof BufferedHttpServletRequest) {
                body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
            }
        } catch (IOException e) {
            LOGGER.error("printRequest 获取body异常...", e);
        }
        JSONObject requestJ = new JSONObject();
        JSONObject headers = new JSONObject();
        Collections.list(request.getHeaderNames())
                .stream()
                .forEach(name -> headers.put(name, request.getHeader(name)));
        requestJ.put("headers", headers);
        requestJ.put("parameters", request.getParameterMap());
        requestJ.put("body", body);
        requestJ.put("remote-user", request.getRemoteUser());
        requestJ.put("remote-addr", request.getRemoteAddr());
        requestJ.put("remote-host", request.getRemoteHost());
        requestJ.put("remote-port", request.getRemotePort());
        requestJ.put("uri", request.getRequestURI());
        requestJ.put("url", request.getRequestURL());
        requestJ.put("servlet-path", request.getServletPath());
        requestJ.put("method", request.getMethod());
        requestJ.put("query", request.getQueryString());
        requestJ.put("path-info", request.getPathInfo());
        requestJ.put("context-path", request.getContextPath());
//        LOGGER.info("Request-Info: " + JSON.toJSONString(requestJ, SerializerFeature.PrettyFormat));
    }
}
src/main/java/com/common/redis/config/CommonRedisConfig.java
New file
@@ -0,0 +1,62 @@
package com.common.redis.config;
import com.common.redis.util.RedisUtil;
import com.common.redis.serializer.KryoRedisSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
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.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
 * @author xiaobzhou
 * date 2019-07-10 19:00
 * {@link RedisAutoConfiguration}
 */
@Configuration
@ConditionalOnProperty(value = "spring.redis.host")
public class CommonRedisConfig {
    /**
     * 设置redis配置项
     *
     * @param connectionFactory
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(name = "template")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer valueSerializer = new KryoRedisSerializer(Object.class);
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(valueSerializer);
        template.setValueSerializer(valueSerializer);
        template.afterPropertiesSet();
        return template;
    }
    /**
     * 获取redis 工具类
     *
     * @param template
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public RedisUtil redisUtil(RedisTemplate<String, Object> template) {
        return new RedisUtil(template);
    }
}
src/main/java/com/common/redis/serializer/KryoRedisSerializer.java
New file
@@ -0,0 +1,94 @@
package com.common.redis.serializer;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.io.ByteArrayOutputStream;
/**
 * @author xiaobzhou
 * date 2019-07-10 19:00
 */
public class KryoRedisSerializer<T> implements RedisSerializer<T> {
    private static final Logger LOG = LoggerFactory.getLogger(KryoRedisSerializer.class);
    public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
    /**
     * 初始化KRYO对象
     */
    private static final ThreadLocal<Kryo> KRYO = ThreadLocal.withInitial(Kryo::new);
    private Class<T> classType;
    public void cleanKryo() {
        KRYO.remove();
    }
    public KryoRedisSerializer(Class<T> classType) {
        super();
        this.classType = classType;
    }
    /**
     * Kryo序列化方法
     *
     * @param o
     * @return
     * @throws SerializationException
     */
    @Override
    public byte[] serialize(T o) throws SerializationException {
        if (o == null) {
            return EMPTY_BYTE_ARRAY;
        }
        Kryo kryo = KRYO.get();
        kryo.setReferences(false);
        kryo.register(classType);
        try {
            ByteArrayOutputStream byteOps = new ByteArrayOutputStream();
            Output output = new Output(byteOps);
            kryo.writeClassAndObject(output, o);
            output.flush();
            return byteOps.toByteArray();
        } catch (Exception ex) {
            LOG.error(ex.getMessage(), ex);
        }
        return EMPTY_BYTE_ARRAY;
    }
    /**
     * Kryo反序列化方法
     *
     * @param bytes
     * @return
     * @throws SerializationException
     */
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes != null) {
            Kryo kryo = KRYO.get();
            kryo.setReferences(false);
            kryo.register(classType);
            try {
                Input input = new Input(bytes);
                return (T) kryo.readClassAndObject(input);
            } catch (Exception ex) {
                LOG.error(ex.getMessage(), ex);
            }
        }
        return null;
    }
}
src/main/java/com/common/redis/util/RedisLockUtil.java
New file
@@ -0,0 +1,182 @@
package com.common.redis.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.Objects;
public class RedisLockUtil {
    private final static Logger log = LoggerFactory.getLogger(RedisLockUtil.class);
    private RedisTemplate<String, Object> redisTemplate;
    public RedisLockUtil(RedisTemplate<String, Object> template) {
        this.redisTemplate = template;
    }
    private static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 100;
    /**
     * 锁超时时间,防止线程在入锁以后,无限的执行等待
     */
    private int expireMsecs = 60 * 1000;
    /**
     * 锁等待时间,防止线程饥饿
     */
    private int timeoutMsecs = 10 * 1000;
    private volatile boolean locked = false;
    private String get(final String key) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    byte[] data = connection.get(serializer.serialize(key));
                    connection.close();
                    if (data == null) {
                        return null;
                    }
                    return serializer.deserialize(data);
                }
            });
        } catch (Exception e) {
            log.error("get redis error, key : {}", key);
        }
        return obj != null ? obj.toString() : null;
    }
    private boolean setNX(final String key, final String value) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    Boolean success = connection.setNX(serializer.serialize(key), serializer.serialize(value));
                    connection.close();
                    return success;
                }
            });
        } catch (Exception e) {
            log.error("setNX redis error, key : {}", key);
        }
        return obj != null ? (Boolean) obj : false;
    }
    private String getSet(final String key, final String value) {
        Object obj = null;
        try {
            obj = redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    StringRedisSerializer serializer = new StringRedisSerializer();
                    byte[] ret = connection.getSet(serializer.serialize(key), serializer.serialize(value));
                    connection.close();
                    return serializer.deserialize(ret);
                }
            });
        } catch (Exception e) {
            log.error("setNX redis error, key : {}", key);
        }
        return obj != null ? (String) obj : null;
    }
    /**
     * 获得 lock.
     * 实现思路: 主要是使用了redis 的setnx命令,缓存了锁.
     * reids缓存的key是锁的key,所有的共享, value是锁的到期时间(注意:这里把过期时间放在value了,没有时间上设置其超时时间)
     * 执行过程:
     * 1.通过setnx尝试设置某个key的值,成功(当前没有这个锁)则返回,成功获得锁
     * 2.锁已经存在则获取锁的到期时间,和当前时间比较,超时的话,则设置新的值
     *
     * @return true if lock is acquired, false acquire timeouted
     * @throws InterruptedException in case of thread interruption
     */
    public synchronized boolean lock(String lockKey) throws InterruptedException {
        int timeout = timeoutMsecs;
        while (timeout >= 0) {
            long expires = System.currentTimeMillis() + expireMsecs + 1;
            String expiresStr = String.valueOf(expires); //锁到期时间
            if (this.setNX(lockKey, expiresStr)) {
                // lock acquired
                locked = true;
                return true;
            }
            String currentValueStr = this.get(lockKey); //redis里的时间
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
                // lock is expired
                String oldValueStr = this.getSet(lockKey, expiresStr);
                //获取上一个锁到期时间,并设置现在的锁到期时间,
                //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    //防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受
                    //[分布式的情况下]:如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
                    // lock acquired
                    locked = true;
                    return true;
                }
            }
            timeout -= DEFAULT_ACQUIRY_RESOLUTION_MILLIS;
            /*
                延迟100 毫秒,  这里使用随机时间可能会好一点,可以防止饥饿进程的出现,即,当同时到达多个进程,
                只会有一个进程获得锁,其他的都用同样的频率进行尝试,后面有来了一些进行,也以同样的频率申请锁,这将可能导致前面来的锁得不到满足.
                使用随机的等待时间可以一定程度上保证公平性
             */
            Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
        }
        return false;
    }
    /**
     * Acqurired lock release.
     */
    public synchronized void unlock(String lockKey) {
        if (locked) {
            redisTemplate.delete(lockKey);
            locked = false;
        }
    }
    public boolean lock(String lockKey, long lockExpireMils) {
        return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
            long nowTime = System.currentTimeMillis();
            Boolean acquire = connection.set(lockKey.getBytes(), String.valueOf(nowTime + lockExpireMils + 1).getBytes());
            if (acquire) {
                return Boolean.TRUE;
            } else {
                byte[] value = connection.get(lockKey.getBytes());
                if (Objects.nonNull(value) && value.length > 0) {
                    long oldTime = Long.parseLong(new String(value));
                    if (oldTime < nowTime) {
                        //connection.getSet:返回这个key的旧值并设置新值。
                        byte[] oldValue = connection.getSet(lockKey.getBytes(), String.valueOf(nowTime + lockExpireMils + 1).getBytes());
                        //当key不存时会返回空,表示key不存在或者已在管道中使用
                        return oldValue == null ? false : Long.parseLong(new String(oldValue)) < nowTime;
                    }
                }
            }
            return Boolean.FALSE;
        });
    }
}
src/main/java/com/common/redis/util/RedisUtil.java
New file
@@ -0,0 +1,283 @@
package com.common.redis.util;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
 * redis工具类
 *
 * @author lisheng
 * @Date 2020/08/9 15:31
 */
public class RedisUtil {
    private RedisTemplate<String, Object> template;
    public RedisUtil(RedisTemplate<String, Object> template) {
        this.template = template;
    }
    /**
     * 设置过期时间
     *
     * @param key
     * @param timeout
     * @return
     */
    public boolean expire(String key, long timeout) {
        try {
            if (timeout > 0) {
                template.expire(key, timeout, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception ex) {
            return false;
        }
    }
    /**
     * 获取过期时间
     *
     * @param key
     * @return
     */
    public long getExpire(String key) {
        return template.getExpire(key, TimeUnit.SECONDS);
    }
    /**
     * key是否存在
     *
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        try {
            return template.hasKey(key);
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * 删除key
     *
     * @param key
     */
    public void deleteKey(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                template.delete(key[0]);
            } else {
                template.delete(Arrays.asList(key));
            }
        }
    }
    /**
     * 根据key获取value
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        if (key == null) {
            return null;
        }
        return template.opsForValue().get(key);
    }
    /**
     * 根据key和类型获取value
     *
     * @param key
     * @param classType
     * @param <T>
     * @return
     */
    public <T> T getObject(String key, Class<T> classType) {
        return (T) this.get(key);
    }
    /**
     * 根据key和类型获取value list
     *
     * @param key
     * @param classType
     * @param <T>
     * @return
     */
    public <T> List<T> getObjects(String key, Class<T> classType) {
        return (List<T>) get(key);
    }
    /**
     * 设置缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(String key, Object value) {
        try {
            template.opsForValue().set(key, value);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }
    /**
     * 设置过期时间缓存
     *
     * @param key
     * @param value
     * @param timeout
     * @return
     */
    public boolean set(String key, Object value, long timeout) {
        try {
            if (timeout > 0) {
                template.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
            } else {
                return set(key, value);
            }
            return true;
        } catch (Exception ex) {
            return false;
        }
    }
    /**
     * 获取Redis列表
     *
     * @param key
     * @return
     */
    public Object lGet(String key) {
        if (key == null) {
            return null;
        }
        return template.opsForList().rightPop(key);
    }
    /**
     * 设置缓存列表类型
     *
     * @param key
     * @param values
     * @return
     */
    public boolean lSet(String key, List<?> values) {
        try {
            template.opsForList().rightPush(key, values);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }
    /**
     * 设置hashmap类型缓存
     *
     * @param key
     * @param field
     * @param value
     * @param <T>
     * @return
     */
    public <T> boolean hset(String key, String field, T value) {
        template.opsForHash().put(key, field, value);
        return true;
    }
    public <T> boolean hset(String key, Object field, T value) {
        template.opsForHash().put(key, field, value);
        return true;
    }
    /**
     * 按hashmap key获取缓存中的数据
     *
     * @param key
     * @param fields
     * @return
     */
    public List<Object> hashMultiGet(String key, Collection<Object> fields) {
        if (key == null) {
            return null;
        }
        return template.opsForHash().multiGet(key, fields);
    }
    /**
     * 获取单个hashmap中的值
     *
     * @param key
     * @param field
     * @return
     */
    public Object hashSingleGet(String key, Object field) {
        if (key == null) {
            return null;
        }
        return template.opsForHash().get(key, field);
    }
    /**
     * 获取单个hashmap中的值
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hashGetAll(String key) {
        if (key == null) {
            return null;
        }
        return template.opsForHash().entries(key);
    }
    /**
     * delete 指定某些hashmap中的值
     *
     * @param key
     * @param fields
     * @return
     */
    public Object hDelFields(String key, Object... fields) {
        if (key == null) {
            return null;
        }
        return template.opsForHash().delete(key, fields);
    }
    /**
     * 缓存自增
     *
     * @param key
     * @param delta
     * @return
     */
    public Long incr(String key, long delta) {
        return template.opsForValue().increment(key, delta);
    }
    public Set<String> getKeys(String prex){
        return template.keys(prex);
    }
}
src/main/java/com/common/security/configure/AppDetails.java
New file
@@ -0,0 +1,72 @@
package com.common.security.configure;
import com.deloitte.system.model.AppConfig;
import com.deloitte.system.model.AppPermissionConfig;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.stream.Collectors;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
public class AppDetails implements UserDetails {
    private AppConfig appConfig;
    private String userid;
    public AppDetails(AppConfig appConfig){
        this.appConfig = appConfig;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return AppPermissionConfig.dao.findByAppidList(appConfig.appid()).stream()
                .filter(app -> app.url() != null)
                .map(app -> new SimpleGrantedAuthority(app.url()))
                .collect(Collectors.toList());
    }
    @Override
    public String getPassword() {
        return appConfig.appsecret();
    }
    @Override
    public String getUsername() {
        return appConfig.appid();
    }
    public void setUserid(String userid){
        this.userid = userid;
    }
    public String getUserid(){
        return userid;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}
src/main/java/com/common/security/configure/RestAuthenticationEntryPoint.java
New file
@@ -0,0 +1,36 @@
package com.common.security.configure;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author 廖振钦
 * @date 2022-01-17
 * token失效访问接口时,自定义的返回结果
 */
@Component
public class RestAuthenticationEntryPoint  implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.addHeader("Content-Security-Policy","default-src 'self'");
        response.addHeader("Strict-Transport-Security","max-age=31536000; includeSubdomains");
        response.addHeader("Referrer-Policy","no-referrer-when-downgrade");
        response.addHeader("X-Permitted-Cross-Domain-Policies","all");
        response.addHeader("X-Download-Options","noopen");
        Result res=Result.respErr(ResultCodeEnum.RT_INVALID_TOKEN);
        response.getWriter().println(JSONObject.toJSONString(res));
        response.getWriter().flush();
    }
}
src/main/java/com/common/security/configure/RestfulAccessDeniedHandler.java
New file
@@ -0,0 +1,35 @@
package com.common.security.configure;
import com.alibaba.fastjson.JSONObject;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException e) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.addHeader("Content-Security-Policy","default-src 'self'");
        response.addHeader("Strict-Transport-Security","max-age=31536000; includeSubdomains");
        response.addHeader("Referrer-Policy","no-referrer-when-downgrade");
        response.addHeader("X-Permitted-Cross-Domain-Policies","all");
        response.addHeader("X-Download-Options","noopen");
        Result res=Result.respErr(ResultCodeEnum.RT_ACCESS_DENIED);
        response.getWriter().println(JSONObject.toJSONString(res));
        response.getWriter().flush();
    }
}
src/main/java/com/common/security/configure/RoleBasedVoter.java
New file
@@ -0,0 +1,100 @@
package com.common.security.configure;
import com.common.annotation.NoAuthorize;
import com.common.annotation.NoToken;
import com.common.core.utils.SpringContextUtils;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
public class RoleBasedVoter implements AccessDecisionVoter<Object> {
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        FilterInvocation fi = (FilterInvocation) object;
        HttpServletRequest fiRequest = fi.getRequest();
        String requestURI = fiRequest.getRequestURI();
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = ((RequestMappingHandlerMapping)SpringContextUtils.getBean("requestMappingHandlerMapping")).getHandlerMethods();
        Set<String> anonymousUrls = new HashSet<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethods.entrySet()) {
            // 获取所有接口的方法
            HandlerMethod handlerMethod = infoEntry.getValue();
            if ( handlerMethod.hasMethodAnnotation(NoAuthorize.class)) {
                PatternsRequestCondition requestCondition = infoEntry.getKey().getPatternsCondition();
                Optional.ofNullable(requestCondition).orElseThrow(RuntimeException::new);
                anonymousUrls.addAll(requestCondition.getPatterns().stream().map(m -> SpringContextUtils.getEnvParam("server.servlet.context-path")+m).collect(Collectors.toList()));
            }
            if ( handlerMethod.hasMethodAnnotation(NoToken.class)) {
                PatternsRequestCondition requestCondition = infoEntry.getKey().getPatternsCondition();
                Optional.ofNullable(requestCondition).orElseThrow(RuntimeException::new);
                anonymousUrls.addAll(requestCondition.getPatterns().stream().map(m -> SpringContextUtils.getEnvParam("server.servlet.context-path")+m).collect(Collectors.toList()));
            }
        }
        for(String item : Arrays.asList(new String[]{
                ".html",
                ".css",
                ".js",
                ".png",
                ".ttf",
                ".ico",
                ".woff",
                "/swagger-resources",
                "/v2/api-docs",
                "/druid"
        })){
            if(requestURI.contains(item)){
                return ACCESS_GRANTED;
            }
        }
        if(anonymousUrls.contains(requestURI)){
            return ACCESS_GRANTED;
        }
        if(authentication == null) {
            return ACCESS_DENIED;
        }
        int result = ACCESS_ABSTAIN;
        Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
        for (ConfigAttribute attribute : attributes) {
            if (this.supports(attribute)) {
                //TODO 目前是所有请求都要判断权限,如后续有不需要判断的,可以启动时从数据库加载到缓存
                result = ACCESS_DENIED;
                // Attempt to find a matching granted authority
                for (GrantedAuthority authority : authorities) {
                    if (requestURI.equals(authority.getAuthority())) {
                        return ACCESS_GRANTED;
                    }
                }
            }
        }
        return result;
    }
    Collection<? extends GrantedAuthority> extractAuthorities(
        Authentication authentication) {
        return authentication.getAuthorities();
    }
    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}
src/main/java/com/common/security/configure/SecurityConfig.java
New file
@@ -0,0 +1,128 @@
package com.common.security.configure;
import com.common.annotation.NoToken;
import com.common.core.utils.SpringContextUtils;
import com.common.filter.CorsFilter;
import com.common.filter.JwtAuthenticationTokenFilter;
import com.deloitte.system.model.AppConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    // 放行所有请求
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = ((RequestMappingHandlerMapping)SpringContextUtils.getBean("requestMappingHandlerMapping")).getHandlerMethods();
        Set<String> anonymousUrls = new HashSet<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethods.entrySet()) {
            // 获取所有接口的方法
            HandlerMethod handlerMethod = infoEntry.getValue();
            if (handlerMethod.hasMethodAnnotation(NoToken.class)) {
                PatternsRequestCondition requestCondition = infoEntry.getKey().getPatternsCondition();
                Optional.ofNullable(requestCondition).orElseThrow(RuntimeException::new);
                anonymousUrls.addAll(requestCondition.getPatterns().stream().map(m -> m).collect(Collectors.toList()));
            }
        }
        anonymousUrls.add("/druid/**");
        httpSecurity.csrf()
                .disable()
                .sessionManagement()// 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问
                        "/",
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-resources/**",
                        "/v2/api-docs/**",
                        "/druid/**"
                )
                .permitAll()
                .antMatchers(anonymousUrls.toArray(new String[0]))
                .anonymous()
                .anyRequest()// 除上面外的所有请求全部需要鉴权认证
                .authenticated()
                // 自定义accessDecisionManager
                .accessDecisionManager(accessDecisionManager());
                ;//放行option请求
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        // 添加跨域 filter
        httpSecurity.addFilterBefore(corsFilter(), JwtAuthenticationTokenFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);
    }
    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }
    @Bean
    public CorsFilter corsFilter() {
        return new CorsFilter();
    }
    @Bean
    public UserDetailsService userDetailsService() {
        //获取登录用户信息
        return username -> {
            AppConfig appConfig = AppConfig.dao.findByAppid(username);
            return new AppDetails(appConfig);
        };
    }
    @Bean
    public AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters
                = Arrays.asList(
                new WebExpressionVoter(),
                // new RoleVoter(),
                new RoleBasedVoter(),
                new AuthenticatedVoter());
        return new UnanimousBased(decisionVoters);
    }
}
src/main/java/com/common/security/utils/IpUtil.java
New file
@@ -0,0 +1,48 @@
package com.common.security.utils;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
@Slf4j
public class IpUtil {
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        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.getHeader("http_client_ip");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        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("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip != null && ip.indexOf(",") != -1) {
            ip = ip.substring(ip.lastIndexOf(",") + 1, ip.length()).trim();
        }
        return ip;
    }
}
src/main/java/com/common/security/utils/SecurityHolderUtils.java
New file
@@ -0,0 +1,20 @@
package com.common.security.utils;
/**
 * @author 廖振钦
 * @date 2022-01-19
 */
public class SecurityHolderUtils {
    private static final ThreadLocal<String> USER_ID = new ThreadLocal<>();
    public static void setUserId(String userid){
        USER_ID.set(userid);
    }
    public static String getUserId(){
        return USER_ID.get();
    }
    public static void cleanUserId(){
        USER_ID.remove();
    }
}
src/main/java/com/common/security/utils/SecurityUtils.java
New file
@@ -0,0 +1,37 @@
package com.common.security.utils;
import com.common.security.configure.AppDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
/**
 * @author 廖振钦
 * @date 2022-01-19
 */
public class SecurityUtils {
    public static AppDetails getAppDetails(){
        try {
            SecurityContext ctx = SecurityContextHolder.getContext();
            Authentication auth = ctx.getAuthentication();
            AppDetails details = (AppDetails) auth.getPrincipal();
            return details;
        } catch (Exception e) {
            return null;
        }
    }
    public static String getAppid(){
        AppDetails appDetails=getAppDetails();
        return appDetails ==null ? null : appDetails.getUsername();
    }
    public static String getUserId(){
        return SecurityHolderUtils.getUserId();
    }
    public static String byId(){
        return SecurityHolderUtils.getUserId() == null ? getAppid() : SecurityHolderUtils.getUserId();
    }
}
src/main/java/com/common/servlet/JFinalStatViewServlet.java
New file
@@ -0,0 +1,96 @@
package com.common.servlet;
import com.alibaba.druid.support.http.StatViewServlet;
import com.jfinal.plugin.druid.IDruidStatViewAuth;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@WebServlet
public class JFinalStatViewServlet extends StatViewServlet {
    private static final long serialVersionUID = 2898674199964021798L;
    private IDruidStatViewAuth auth = new IDruidStatViewAuth(){
        public boolean isPermitted(HttpServletRequest request) {
            return true;
        }
    };
    private String visitPath = "/admin/druid/monitor";
    public JFinalStatViewServlet() {
    }
    public boolean isPermittedRequest(HttpServletRequest request) {
        return this.auth.isPermitted(request);
    }
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String contextPath = request.getContextPath();
        String requestURI = request.getRequestURI();
        response.setCharacterEncoding("utf-8");
        if (contextPath == null) {
            contextPath = "";
        }
        int index = contextPath.length() + this.visitPath.length();
        String uri = requestURI.substring(0, index);
        String path = requestURI.substring(index);
        if (!this.isPermittedRequest(request)) {
            path = "/nopermit.html";
            this.returnResourceFile(path, uri, response);
        } else {
            String fullUrl;
            if ("/submitLogin".equals(path)) {
                fullUrl = request.getParameter("loginUsername");
                String passwordParam = request.getParameter("loginPassword");
                response.getWriter().print("success");
//                if (this.handler.username.equals(fullUrl) && this.handler.password.equals(passwordParam)) {
//                    request.getSession().setAttribute("druid-user", this.handler.username);
//                    response.getWriter().print("success");
//                } else {
//                    response.getWriter().print("error");
//                }
            } else if (this.isRequireAuth() && !this.ContainsUser(request) && !"/login.html".equals(path) && !path.startsWith("/css") && !path.startsWith("/js") && !path.startsWith("/img")) {
                if (contextPath != null && !contextPath.equals("") && !contextPath.equals("/")) {
                    if ("".equals(path)) {
                        response.sendRedirect("druid/login.html");
                    } else {
                        response.sendRedirect("login.html");
                    }
                } else {
                    response.sendRedirect("/druid/login.html");
                }
            } else if (!"".equals(path)) {
                if ("/".equals(path)) {
                    response.sendRedirect("index.html");
                } else if (path.indexOf(".json") >= 0) {
                    fullUrl = path;
                    if (request.getQueryString() != null && request.getQueryString().length() > 0) {
                        fullUrl = path + "?" + request.getQueryString();
                    }
                    response.getWriter().print(this.process(fullUrl));
                } else {
                    this.returnResourceFile(path, uri, response);
                }
            } else {
                if (contextPath != null && !contextPath.equals("") && !contextPath.equals("/")) {
                    response.sendRedirect("druid/index.html");
                } else {
                    response.sendRedirect("/druid/index.html");
                }
            }
        }
    }
}
src/main/java/com/common/sign/BufferedHttpServletRequest.java
New file
@@ -0,0 +1,86 @@
package com.common.sign;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
public class BufferedHttpServletRequest extends HttpServletRequestWrapper {
    private ByteBuf buffer;
    private final AtomicBoolean isCached = new AtomicBoolean();
    public BufferedHttpServletRequest(HttpServletRequest request, int initialCapacity) {
        super(request);
        int contentLength = request.getContentLength();
        int min = Math.min(initialCapacity, contentLength);
        if (min < 0) {
            buffer = Unpooled.buffer(0);
        } else {
            buffer = Unpooled.buffer(min, contentLength);
        }
    }
    //TODO NettyServletInputStream find
    @Override
    public ServletInputStream getInputStream() throws IOException {
        //Only returning data from buffer if it is readonly, which means the underlying stream is EOF or closed.
//        if (isCached.get()) {
//            return new NettyServletInputStream(buffer);
//        }
        return new ContentCachingInputStream(super.getInputStream());
    }
    public void release() {
        buffer.release();
    }
    private class ContentCachingInputStream extends ServletInputStream {
        private final ServletInputStream is;
        public ContentCachingInputStream(ServletInputStream is) {
            this.is = is;
        }
        @Override
        public int read() throws IOException {
            int ch = this.is.read();
            if (ch != -1) { //Stream is EOF, set this buffer to readonly state
                buffer.writeByte(ch);
            } else {
                isCached.compareAndSet(false, true);
            }
            return ch;
        }
        @Override
        public void close() throws IOException {
            //Stream is closed, set this buffer to readonly state
            try {
                is.close();
            } finally {
                isCached.compareAndSet(false, true);
            }
        }
        @Override
        public boolean isFinished() {
            throw new UnsupportedOperationException("Not yet implemented!");
        }
        @Override
        public boolean isReady() {
            throw new UnsupportedOperationException("Not yet implemented!");
        }
        @Override public void setReadListener(ReadListener readListener) {
            throw new UnsupportedOperationException("Not yet implemented!");
        }
    }
}
src/main/java/com/deloitte/sbg/controller/SapSBGRest.groovy
New file
@@ -0,0 +1,174 @@
package com.deloitte.sbg.controller
import cn.hutool.core.util.StrUtil
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.common.annotation.NoToken
import com.common.core.enums.ResultCodeEnum
import com.common.core.exception.BizException
import com.alibaba.fastjson.serializer.SerializerFeature
import com.common.core.utils.IdUtils
import com.common.core.utils.RestUtil
import com.common.core.utils.SecretsManagerUtils
import com.common.core.utils.SpringContextUtils
import com.deloitte.system.model.Contact
import com.deloitte.system.model.Opportunity
import groovy.util.logging.Log
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.env.Environment
import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.client.HttpServerErrorException
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import javax.servlet.http.HttpServletResponse
import java.util.logging.Level
/**
 * @Author: Ben Pi
 * @Date: 23/02/2022 13:50
 */
@RestController
@RequestMapping("/sbg")
@Log("log")
class SapSBGRest {
    @Value("\${sap.url}")
    private String sapurl;
    @Autowired
    private IdUtils idUtils;
    @Autowired
    private Environment env;
    @Autowired
    private SecretsManagerUtils secretsManagerUtils;
    @RequestMapping(value = "/001", method = RequestMethod.POST)
    def sbg001(@RequestBody def body) {
        log.info("001接口请求=====================body:"+body)
        HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()
        JSONObject object = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.systemauth"));
        String saptoken = object.getString("sap_token");
        try {
            for (int i = 0; i < body.SSBDCustomerContacts.SSBDCustomerContact.size(); i++) {
                //判断是客户还是联系人,客户信息CustomerCode和ContactCode相等,不需要做解密
                String customerCode = body.SSBDCustomerContacts.SSBDCustomerContact[i].CustomerCode
                String contactCode = body.SSBDCustomerContacts.SSBDCustomerContact[i].ContactCode
                if(customerCode.equals(contactCode)){
                    if (body.SSBDCustomerContacts.SSBDCustomerContact[i].DataId != null) {
                        body.SSBDCustomerContacts.SSBDCustomerContact[i].remove("DataId")
                    }
                    continue
                }
                Contact contact = new Contact().findById(body.SSBDCustomerContacts.SSBDCustomerContact[i].DataId.toString())
                if (!contact) {
                    throw new BizException("无效的dataId")
                }
                String contactOffice = contact.getLastName()
                String telephoneMobile = (StrUtil.isBlank(contact.getPhone())?"":contact.getPhone())+","+(StrUtil.isBlank(contact.getMobilePhone())?"":contact.getMobilePhone())
                String address = contact.getAddress1()
                String postalCode = contact.getPostcode()
                String faxEmail = (StrUtil.isBlank(contact.getFax())?"":contact.getFax())+","+(StrUtil.isBlank(contact.getEmail())?"":contact.getEmail())
                body.SSBDCustomerContacts.SSBDCustomerContact[i].ContactOffice = contactOffice
                body.SSBDCustomerContacts.SSBDCustomerContact[i].TelephoneMobile = telephoneMobile
                body.SSBDCustomerContacts.SSBDCustomerContact[i].Address = address
                body.SSBDCustomerContacts.SSBDCustomerContact[i].PostalCode = postalCode
                body.SSBDCustomerContacts.SSBDCustomerContact[i].FaxEmail = faxEmail
                if (body.SSBDCustomerContacts.SSBDCustomerContact[i].DataId != null) {
                    body.SSBDCustomerContacts.SSBDCustomerContact[i].remove("DataId")
                }
            }
            HttpHeaders header = new HttpHeaders()
            header.add("Authorization", "Basic " + saptoken)
            ResponseEntity sapRes= RestUtil.postNative(sapurl + "SBG001", header, null, JSONObject.toJSONString(body, SerializerFeature.WriteMapNullValue))
            resp.setStatus(sapRes.getStatusCodeValue())
            return "success"
        } catch(HttpServerErrorException e){
            log.log(new Level("ERROR", Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "001请求异常", e)
            resp.setStatus(e.getRawStatusCode())
            return JSON.parse(e.getResponseBodyAsString())
        } catch (Exception e) {
            log.log(new Level("ERROR",Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "001请求异常", e)
            resp.setStatus(Integer.valueOf(ResultCodeEnum.AWS_RT_ERROR.getCode()))
            throw new BizException(ResultCodeEnum.AWS_RT_ERROR)
        }
    }
    @RequestMapping(value = "/027", method = RequestMethod.POST)
    def sbg027(@RequestBody def body) {
        log.info("027接口请求=====================body:"+body)
        HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()
        JSONObject object = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.systemauth"));
        String saptoken = object.getString("sap_token");
        log.info("saptoken:"+saptoken);
        try {
            def datas = body.GeDatas
            for (int i = 0; i < datas.GeData.size(); i++) {
                Opportunity opportunity = new Opportunity().findById(datas.GeData[i].DataId)
                if (!opportunity) {
                    throw new BizException("无效的dataId")
                }
                datas.GeData[i].DealerSalesStaffName = opportunity.getDealerSalesStaffName()
                if (datas.GeData[i].DataId != null) {
                    datas.GeData[i].remove("DataId")
                }
            }
            HttpHeaders header = new HttpHeaders()
            header.add("Authorization", "Basic " + saptoken)
            ResponseEntity sapRes = RestUtil.postNativeObject(sapurl + "SBG027", header, null, JSONObject.toJSONString(body, SerializerFeature.WriteMapNullValue))
            resp.setStatus(sapRes.getStatusCodeValue())
            return "success"
        } catch(HttpServerErrorException e){
            log.log(new Level("ERROR", Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "027请求异常", e)
            resp.setStatus(e.getRawStatusCode())
            return JSON.parse(e.getResponseBodyAsString())
        } catch (Exception e) {
            log.log(new Level("ERROR",Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "027请求异常", e)
            resp.setStatus(Integer.valueOf(ResultCodeEnum.AWS_RT_ERROR.getCode()))
            throw new BizException(ResultCodeEnum.AWS_RT_ERROR)
        }
    }
    @RequestMapping(value = "/007", method = RequestMethod.POST)
    def sbg007(@RequestBody def body) {
        log.info("007接口请求=====================body:"+body)
        HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()
        JSONObject object = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.systemauth"));
        String saptoken = object.getString("sap_token");
        try {
            def datas = body.GeDatas
            for (int i = 0; i < datas.GeData.size(); i++) {
                Opportunity opportunity = new Opportunity().findById(datas.GeData[i].DataId)
                if (!opportunity) {
                    throw new BizException("无效的dataId")
                }
                datas.GeData[i].DealerSalesStaffName = opportunity.getDealerSalesStaffName()
                if (datas.GeData[i].DataId != null) {
                    datas.GeData[i].remove("DataId")
                }
            }
            HttpHeaders header = new HttpHeaders()
            header.add("Authorization", "Basic " + saptoken)
            ResponseEntity sapRes = RestUtil.postNative(sapurl + "SBG007", header, null, JSONObject.toJSONString(body, SerializerFeature.WriteMapNullValue))
            resp.setStatus(sapRes.getStatusCodeValue())
            return "success"
        } catch(HttpServerErrorException e){
            log.log(new Level("ERROR", Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "007请求异常", e)
            resp.setStatus(e.getRawStatusCode())
            return JSON.parse(e.getResponseBodyAsString())
        } catch (Exception e) {
            log.log(new Level("ERROR",Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "007请求异常", e)
            resp.setStatus(Integer.valueOf(ResultCodeEnum.AWS_RT_ERROR.getCode()))
            throw new BizException(ResultCodeEnum.AWS_RT_ERROR)
        }
    }
}
src/main/java/com/deloitte/sbg/controller/SfSBGRest.groovy
New file
@@ -0,0 +1,182 @@
package com.deloitte.sbg.controller
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONObject
import com.alibaba.fastjson.serializer.SerializerFeature
import com.common.core.domain.DesensitiveType
import com.common.core.enums.ResultCodeEnum
import com.common.core.service.SFService
import com.common.core.utils.DesensitiveUtils
import com.common.core.utils.IdUtils
import com.common.core.utils.RestUtil
import com.common.core.utils.StringUtils
import com.deloitte.system.model.Contact
import com.deloitte.system.request.SFTokenDto
import groovy.util.logging.Log
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.client.HttpServerErrorException
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import org.springframework.http.HttpHeaders
import javax.servlet.http.HttpServletResponse
import java.util.logging.Level
import static com.jfinal.plugin.activerecord.Db.tx
/**
 * @Author: Ben Pi
 * @Date: 22/02/2022 14:14
 */
@RestController
@RequestMapping("/sbg")
@Log("log")
class SfSBGRest {
    @Autowired
    private SFService sfService;
    @Autowired
    private IdUtils idUtils;
    @Value("\${salesforce.sbgurl}")
    private String uri;
    @RequestMapping(value = "/203", method = RequestMethod.POST)
    def sbg203(@RequestBody def body) {
        log.info("203接口请求=====================body:"+body)
        HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()
        def result;
        tx({ ->
            SFTokenDto tokenDto = sfService.getToken()
            String accessToken = tokenDto.getAccessToken()
            HttpHeaders header = new HttpHeaders()
            header.add("Authorization", "Bearer " + accessToken)
            try {
                for (int i = 0; i < body.Partners.size(); i++) {
                    body.Partners[i].other1 = body.Partners[i].RegisterAddress
                    if(null == body.Partners[i].Consignee_Info){
                        continue;
                    }
                    String registerAddress = body.Partners[i].RegisterAddress
                    String englishAddress = body.Partners[i].STR_SUPPL1
                    String bpType = body.Partners[i].BPType
                    //置空字段
                    body.Partners[i].Invoice_phone = ""
                    body.Partners[i].Incorporator = ""
                    for (int k = 0; k < body.Partners[i].Consignee_Info.size(); k++) {
                        if (StringUtils.isNotBlank(body.Partners[i].Consignee_Info[k].ContactName)) {
                            //置空字段
                            body.Partners[i].Consignee_Info[k].ContactMobilePhone = ""
                            if(null != body.Partners[i].License_Info){
                                for(int j = 0 ; j < body.Partners[i].License_Info.size(); j++){
                                    body.Partners[i].License_Info[j].Principal = ""
                                    body.Partners[i].License_Info[j].BusinessAddress = ""
                                }
                            }
                            if ("23" == bpType){
                                //bpType 为23时不加密
                                continue
                            }
                            def contactDataId = idUtils.nextId()
                            Contact contact = new Contact()
                            String name = body.Partners[i].Consignee_Info[k].ContactName
                            String contactPhone = body.Partners[i].Consignee_Info[k].ContactPhone
                            String contactAddress = body.Partners[i].Consignee_Info[k].ContactAddress
                            String contactEnglishName = body.Partners[i].Consignee_Info[k].ContactEnglishName
                            // 邮政编码
                            String postalCode = body.Partners[i].Consignee_Info[k].PostalCode
                            contact.setDataId(Long.parseLong(contactDataId))
                            contact.setMobilePhone(contactPhone)
                            contact.setMobilePhoneD(contactPhone)
                            if (StringUtils.isNotBlank(contactEnglishName) && "22".equals(bpType)) {
                                contact.setLastName(name + '(' + contactEnglishName + ')')
                                contact.setContactEnglishName(contactEnglishName)
                                contact.setFirstName(null)
                            } else {
                                contact.setLastName(name)
                                contact.setFirstName(null)
                            }
                            contact.setAddress1("22".equals(bpType) ? registerAddress : contactAddress)
                            contact.setAddress1D("22".equals(bpType) ? registerAddress : contactAddress)
                            contact.setEnglishAddress("22".equals(bpType) ? englishAddress : null)
                            body.Partners[i].RegisterAddress = StringUtils.isNotBlank(registerAddress) ? DesensitiveUtils.getFieldDesensitive(DesensitiveType.ADDRESS) : ""
                            body.Partners[i].Consignee_Info[k].ContactName = StringUtils.isNotBlank(name) ? DesensitiveUtils.getFieldDesensitive(DesensitiveType.CHINESE_NAME) : ""
                            body.Partners[i].Consignee_Info[k].ContactPhone = StringUtils.isNotBlank(contactPhone) ? DesensitiveUtils.getFieldDesensitive(DesensitiveType.MOBILE_PHONE) : ""
                            body.Partners[i].Consignee_Info[k].ContactEnglishName = StringUtils.isNotBlank(contactEnglishName) ? DesensitiveUtils.getFieldDesensitive(DesensitiveType.CHINESE_NAME) : ""
                            //body.Partners[i].Consignee_Info[k].EnglishAddress = null//StringUtils.isNotBlank(contact.getEnglishAddress()) ? DesensitiveUtils.getFieldDesensitive(DesensitiveType.ADDRESS) : ""
                            body.Partners[i].Consignee_Info[k].remove("EnglishAddress")
                            body.Partners[i].Consignee_Info[k].ContactAddress = StringUtils.isNotBlank(contactAddress) ? DesensitiveUtils.getFieldDesensitive(DesensitiveType.ADDRESS) : ""
                            body.Partners[i].Consignee_Info[k].PostalCode = StringUtils.isNotBlank(postalCode) ? DesensitiveUtils.getFieldDesensitive(DesensitiveType.ZIP_CODE) : ""
                            body.Partners[i].RegisterAddressEncrypted = com.common.core.utils.SM4Utils.encryptStr(registerAddress)
                            body.Partners[i].Consignee_Info[k].ContactNameEncrypted = com.common.core.utils.SM4Utils.encryptStr(name)
                            body.Partners[i].Consignee_Info[k].ContactPhoneEncrypted = com.common.core.utils.SM4Utils.encryptStr(contactPhone)
                            body.Partners[i].Consignee_Info[k].ContactAddressEncrypted = com.common.core.utils.SM4Utils.encryptStr(contactAddress)
                            body.Partners[i].Consignee_Info[k].ContactEnglishNameEncrypted = StringUtils.isNotBlank(contactEnglishName) ? com.common.core.utils.SM4Utils.encryptStr(contactEnglishName) : ""
                            //body.Partners[i].Consignee_Info[k].EnglishAddressEncrypted = null//StringUtils.isNotBlank(contact.getEnglishAddress()) ? com.common.core.utils.SM4Utils.encryptStr(englishAddress) : ""
                            body.Partners[i].Consignee_Info[k].remove("EnglishAddressEncrypted")
                            body.Partners[i].Consignee_Info[k].PostalCodeEncrypted = com.common.core.utils.SM4Utils.encryptStr(postalCode)
                            body.Partners[i].Consignee_Info[k].DataId = contactDataId
                            contact.save()
                        }
                    }
                }
                ResponseEntity sfRes = RestUtil.postNative(uri + "SBG203/execute", header, null, JSONObject.toJSONString(body,SerializerFeature.WriteMapNullValue))
                resp.setStatus(sfRes.getStatusCodeValue())
                result = sfRes.getBody().getJSONObject("staticResponse")
                Boolean sfStatus = sfRes.getBody().get("SFStatus")
                if (!sfStatus) {
                    return false
                }
                return true
            } catch(HttpServerErrorException e){
                log.log(new Level("ERROR", Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "203请求异常", e)
                resp.setStatus(e.getRawStatusCode())
                result = JSON.parse(e.getResponseBodyAsString())
                return false
            } catch (Exception e) {
                log.log(new Level("ERROR",Integer.MAX_VALUE, "ch.qos.logback.classic.Logger"), "203请求异常", e)
                resp.setStatus(Integer.valueOf(ResultCodeEnum.AWS_RT_ERROR.getCode()))
                result = [[errorCode: 500, message: e.getMessage()]] as JSONArray
                return false
            }
        })
        return result
    }
    @RequestMapping(value = "/comparaEncrypt", method = RequestMethod.POST)
    def comparaEncrypt(@RequestBody encryptBody){
        def root = new ArrayList()
        for(int i = 0 ; i  < encryptBody.size() ; i++){
            def node = [:]
            def encrypt = encryptBody[i];
            node.encryptStr = encrypt.encryptStr
            node.unencryptStr = encrypt.unencryptStr
            boolean flag = false;
            try{
                flag = com.common.core.utils.SM4Utils.decryptStr(encrypt.encryptStr.toString()).equals(encrypt.unencryptStr.toString())
            }catch(Exception e){
                flag = false;
            }
            if(flag){
                node.ismatch = "true"
            }else{
                node.ismatch = "false"
            }
            root.add(node)
        }
        return root
    }
}
src/main/java/com/deloitte/system/controller/CampaignUserController.java
New file
@@ -0,0 +1,86 @@
package com.deloitte.system.controller;
import com.common.annotation.RequestLimit;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.CampaignUserAllDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.request.CampaignUserDto;
import com.deloitte.system.service.CampaignUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@RestController
@RequestMapping("/campaignuser")
public class CampaignUserController {
    @Autowired
    CampaignUserService campaignUserService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<CampaignUserDto> query(@RequestParam("dataId") String dataId) {
        CampaignUserDto dto = campaignUserService.queryForOne(dataId);
        Result<CampaignUserDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<CampaignUserDto>> insert(@RequestBody List<CampaignUserDto> campaignUserDtoList) {
        String txId = idWorker.nextId();
        List<CampaignUserDto> respList = campaignUserService.insertList(campaignUserDtoList, txId);
        Result<List<CampaignUserDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<CampaignUserDto>> update(@RequestBody List<CampaignUserDto> campaignUserDtoList) {
        String txId = idWorker.nextId();
        List<CampaignUserDto> respList = campaignUserService.updateList(campaignUserDtoList, txId);
        Result<List<CampaignUserDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = campaignUserService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = campaignUserService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<CampaignUserDto>> search(@RequestBody SearchDto searchDto){
        List<CampaignUserDto> dtoList = campaignUserService.search(searchDto.getDataIds());
        Result<List<CampaignUserDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
    @RequestLimit(size = 30)
    @RequestMapping(value = "/batchupload", method = RequestMethod.POST)
    public Result<List<CampaignUserAllDto>> batchUpload(@RequestParam(value = "file") MultipartFile[] files){
        String txId = idWorker.nextId();
        List<CampaignUserAllDto> list=campaignUserService.batchUpload(files,txId);
        Result<List<CampaignUserAllDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(list);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/ConfirmTxController.java
New file
@@ -0,0 +1,42 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.deloitte.system.request.TxConfirmDto;
import com.deloitte.system.service.ConfirmTxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/tx")
public class ConfirmTxController {
    @Autowired
    private ConfirmTxService confirmTxService;
    @RequestMapping(value = "/confirm", method = RequestMethod.POST)
    public Result confirm(@RequestBody TxConfirmDto dto) {
        try {
            Result res = Result.resp(ResultCodeEnum.RT_SUCCESS);
            confirmTxService.confirm(dto);
            return res;
        }catch (BizException e){
            return Result.resp("500", e.getMessage());
        }
    }
    @RequestMapping(value = "/confirmfile", method = RequestMethod.POST)
    public Result confirmFile(@RequestBody TxConfirmDto dto) {
        try {
            Result res = Result.resp(ResultCodeEnum.RT_SUCCESS);
            confirmTxService.confirmFile(dto);
            return res;
        }catch (BizException e){
            return Result.resp("500", e.getMessage());
        }
    }
}
src/main/java/com/deloitte/system/controller/ContactController.java
New file
@@ -0,0 +1,96 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.ContactDto;
import com.deloitte.system.request.ContactSearchDto;
import com.deloitte.system.service.ContactService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/contact")
public class ContactController {
    @Autowired
    ContactService contactService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<ContactDto> query(@RequestParam("dataId") String dataId) {
        ContactDto dto = contactService.queryForOne(dataId);
        Result<ContactDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<ContactDto>> insert(@RequestBody List<ContactDto> contactList) {
        String txId = idWorker.nextId();
        List<ContactDto> respList = contactService.insertList(contactList, txId);
        Result<List<ContactDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    /** 不存缓存,直接入库 **/
    @RequestMapping(value = "/batchInsert", method = RequestMethod.POST)
    public Result<List<ContactDto>> batchInsert(@RequestBody List<ContactDto> contactList) {
        List<ContactDto> respList = contactService.batchInsert(contactList);
        Result<List<ContactDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<ContactDto>> update(@RequestBody List<ContactDto> contactList) {
        String txId = idWorker.nextId();
        List<ContactDto> respList = contactService.updateList(contactList, txId);
        Result<List<ContactDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = contactService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = contactService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<ContactDto>> search(@RequestBody ContactSearchDto contactSearchDto) {
        List<ContactDto> dtos = contactService.searchList(contactSearchDto);
        Result<List<ContactDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtos);
        return res;
    }
    @RequestMapping(value = "/decryptupdate",method = RequestMethod.POST)
    public Result<List<ContactDto>> decryptUpdate(@RequestBody List<ContactDto> contactList){
        List<ContactDto> respList=contactService.decryptUpdate(contactList);
        Result<List<ContactDto>> res =Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(respList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/FileController.java
New file
@@ -0,0 +1,103 @@
package com.deloitte.system.controller;
import com.common.annotation.NoToken;
import com.common.annotation.RequestLimit;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.FileRequest;
import com.deloitte.system.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
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.validation.Valid;
import java.io.IOException;
import java.util.List;
/**
 * @ClassName FileController
 * @Author holfeng
 * @Date 11:23 27/01/2022
 * @Version 1.0
 **/
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
    @Autowired
    private FileService fileService;
    @Autowired
    private IdUtils idWorker;
    @RequestLimit(size = 30)
    @PostMapping("/upload")
    public Result<String> upload(@RequestBody @Valid FileRequest fileRequest) {
        String txId = idWorker.nextId();
        String key=fileService.upload(txId,fileRequest);
        Result<String> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(key);
        res.setTxId(txId);
        return res;
    }
    @RequestLimit(size = 500)
    @PostMapping("/batchupload")
    public Result<List<String>> batchUpload(@RequestBody @Valid List<FileRequest> fileList) {
        String txId = idWorker.nextId();
        List<String> keys=fileService.batchUpload(txId,fileList);
        Result<List<String>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(keys);
        res.setTxId(txId);
        return res;
    }
    @GetMapping("/preview")
    @NoToken
    public void preview(@RequestParam("key") String key){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        fileService.preview(request,response,key);
    }
    @GetMapping("/download")
    @NoToken
    public void download(@RequestParam("key") String key){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        fileService.download(request,response,key);
    }
    @RequestLimit(size = 30)
    @PostMapping("/convert")
    @NoToken
    public void convert(){
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        fileService.convert(request,response);
    }
    @GetMapping("/convert")
    @NoToken
    public void convertget(@RequestParam("from") String from) {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        try {
            response.sendRedirect(from);
        } catch (IOException e) {
            throw new BizException(ResultCodeEnum.AWS_RT_ERROR);
        }
    }
    @PostMapping("/delete")
    public Result deleteFile(@RequestBody List<String> keys){
        fileService.deleteFile(keys);
        return Result.resp(ResultCodeEnum.RT_SUCCESS);
    }
}
src/main/java/com/deloitte/system/controller/LoanerApplicationController.java
New file
@@ -0,0 +1,77 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.LoanerApplicationDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.service.LoanerApplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/loanerapplication")
public class LoanerApplicationController {
    @Autowired
    LoanerApplicationService loanerApplicationService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<LoanerApplicationDto> query(@RequestParam("dataId") String dataId) {
        LoanerApplicationDto dto = loanerApplicationService.queryForOne(dataId);
        Result<LoanerApplicationDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<LoanerApplicationDto>> insert(@RequestBody List<LoanerApplicationDto> loanerApplicationList) {
        String txId = idWorker.nextId();
        List<LoanerApplicationDto> respList = loanerApplicationService.insertList(loanerApplicationList, txId);
        Result<List<LoanerApplicationDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<LoanerApplicationDto>> update(@RequestBody List<LoanerApplicationDto> loanerApplicationList) {
        String txId = idWorker.nextId();
        List<LoanerApplicationDto> respList = loanerApplicationService.updateList(loanerApplicationList, txId);
        Result<List<LoanerApplicationDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = loanerApplicationService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = loanerApplicationService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    //根据多个id和apply_person模糊搜索接口(name=apply_person)
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<LoanerApplicationDto>> search(@RequestBody SearchDto contactSearchDto) {
        List<LoanerApplicationDto> dtos = loanerApplicationService.searchList(contactSearchDto);
        Result<List<LoanerApplicationDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtos);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/LoanerUserController.java
New file
@@ -0,0 +1,74 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.LoanerUserDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.service.LoanerUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/loaneruser")
public class LoanerUserController {
    @Autowired
    LoanerUserService loanerUserService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<LoanerUserDto> query(@RequestParam("dataId") String dataId) {
        LoanerUserDto dto = loanerUserService.queryForOne(dataId);
        Result<LoanerUserDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<LoanerUserDto>> insert(@RequestBody List<LoanerUserDto> loanerUserDtoList) {
        String txId = idWorker.nextId();
        List<LoanerUserDto> respList = loanerUserService.insertList(loanerUserDtoList, txId);
        Result<List<LoanerUserDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<LoanerUserDto>> update(@RequestBody List<LoanerUserDto> loanerUserDtoList) {
        String txId = idWorker.nextId();
        List<LoanerUserDto> respList = loanerUserService.updateList(loanerUserDtoList, txId);
        Result<List<LoanerUserDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = loanerUserService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = loanerUserService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<LoanerUserDto>> search(@RequestBody SearchDto searchDto){
        List<LoanerUserDto> dtoList = loanerUserService.search(searchDto.getDataIds());
        Result<List<LoanerUserDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/MailController.java
New file
@@ -0,0 +1,47 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.MessageVo;
import com.deloitte.system.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * @ClassName FileController
 * @Author holfeng
 * @Date 11:23 27/01/2022
 * @Version 1.0
 **/
@RestController
@RequestMapping("/mail")
@Slf4j
public class MailController {
    @Autowired
    private MailService mailService;
    @Autowired
    private IdUtils idWorker;
    @PostMapping("/sendEmail")
    public Result<Object> sendEmail(@RequestBody MessageVo messageVo) {
        String txId = idWorker.nextId();
        String sfMailMergeId=mailService.sendEmail(txId,messageVo);
        Result<Object> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(sfMailMergeId);
        res.setTxId(txId);
        return res;
    }
//    @GetMapping("/sync")
//    public Result<Object>  syncEmail() {
//        String txId = idWorker.nextId();
//        mailService.receiveImapMail();
//        Result<Object> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
//        res.setTxId(txId);
//        return res;
//    }
}
src/main/java/com/deloitte/system/controller/MailMergeController.java
New file
@@ -0,0 +1,77 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.request.MailMergeDto;
import com.deloitte.system.service.MailMergeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/mailmerge")
public class MailMergeController {
    @Autowired
    MailMergeService mailMergeService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<MailMergeDto> query(@RequestParam("dataId") String dataId) {
        MailMergeDto dto = mailMergeService.queryForOne(dataId);
        Result<MailMergeDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<MailMergeDto>> insert(@RequestBody List<MailMergeDto> mailMergeDtoList) {
        String txId = idWorker.nextId();
        List<MailMergeDto> respList = mailMergeService.insertList(mailMergeDtoList, txId);
        Result<List<MailMergeDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<MailMergeDto>> update(@RequestBody List<MailMergeDto> mailMergeDtoList) {
        String txId = idWorker.nextId();
        List<MailMergeDto> respList = mailMergeService.updateList(mailMergeDtoList, txId);
        Result<List<MailMergeDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = mailMergeService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = mailMergeService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<MailMergeDto>> search(@RequestBody SearchDto searchDto){
        List<MailMergeDto> dtoList = mailMergeService.search(searchDto.getDataIds());
        Result<List<MailMergeDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/OpportunityController.java
New file
@@ -0,0 +1,96 @@
package com.deloitte.system.controller;
import com.common.annotation.NoToken;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.OpportunityDto;
import com.deloitte.system.request.QuotesDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.service.OpportunityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/opportunity")
public class OpportunityController {
    @Autowired
    OpportunityService opportunityService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<OpportunityDto> query(@RequestParam("dataId") String dataId) {
        OpportunityDto dto = opportunityService.queryForOne(dataId);
        Result<OpportunityDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<OpportunityDto>> insert(@RequestBody List<OpportunityDto> opportunityList) {
        String txId = idWorker.nextId();
        List<OpportunityDto> respList = opportunityService.insertList(opportunityList, txId);
        Result<List<OpportunityDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/decryptinsert",method = RequestMethod.POST)
    public Result<List<OpportunityDto>> decryptInsert(@RequestBody List<OpportunityDto> opportunityList){
        List<OpportunityDto> respList = opportunityService.decryptInsert(opportunityList);
        Result<List<OpportunityDto>> res =Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<OpportunityDto>> update(@RequestBody List<OpportunityDto> opportunityList) {
        String txId = idWorker.nextId();
        List<OpportunityDto> respList = opportunityService.updateList(opportunityList, txId);
        Result<List<OpportunityDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = opportunityService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = opportunityService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<OpportunityDto>> search(@RequestBody SearchDto searchDto){
        List<OpportunityDto> dtoList = opportunityService.search(searchDto.getDataIds());
        Result<List<OpportunityDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
    @RequestMapping(value = "/decryptupdate",method = RequestMethod.POST)
    public Result<List<OpportunityDto>> decryptUpdate(@RequestBody List<OpportunityDto> opportunityList){
        List<OpportunityDto> respList=opportunityService.decryptUpdate(opportunityList);
        Result<List<OpportunityDto>> res =Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(respList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/OrderController.java
New file
@@ -0,0 +1,95 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.OpportunityDto;
import com.deloitte.system.request.OrderDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    OrderService orderService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<OrderDto> query(@RequestParam("dataId") String dataId) {
        OrderDto dto = orderService.queryForOne(dataId);
        Result<OrderDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<OrderDto>> insert(@RequestBody List<OrderDto> orderList) {
        String txId = idWorker.nextId();
        List<OrderDto> respList = orderService.insertList(orderList, txId);
        Result<List<OrderDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/decryptinsert",method = RequestMethod.POST)
    public Result<List<OrderDto>> decryptInsert(@RequestBody List<OrderDto> orderList){
        List<OrderDto> respList=orderService.decryptInsert(orderList);
        Result<List<OrderDto>> res =Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<OrderDto>> update(@RequestBody List<OrderDto> orderList) {
        String txId = idWorker.nextId();
        List<OrderDto> respList = orderService.updateList(orderList, txId);
        Result<List<OrderDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = orderService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = orderService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<OrderDto>> search(@RequestBody SearchDto searchDto){
        List<OrderDto> dtoList = orderService.search(searchDto.getDataIds());
        Result<List<OrderDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
    @RequestMapping(value = "/decryptupdate",method = RequestMethod.POST)
    public Result<List<OrderDto>> decryptUpdate(@RequestBody List<OrderDto> orderList){
        List<OrderDto> respList=orderService.decryptUpdate(orderList);
        Result<List<OrderDto>> res =Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(respList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/QuotesController.java
New file
@@ -0,0 +1,76 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.LoanerUserDto;
import com.deloitte.system.request.QuotesDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.service.QuotesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/quotes")
public class QuotesController {
    @Autowired
    QuotesService quotesService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<QuotesDto> query(@RequestParam("dataId") String dataId) {
        QuotesDto dto = quotesService.queryForOne(dataId);
        Result<QuotesDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<QuotesDto>> insert(@RequestBody List<QuotesDto> quotesDtoList) {
        String txId = idWorker.nextId();
        List<QuotesDto> respList = quotesService.insertList(quotesDtoList, txId);
        Result<List<QuotesDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<QuotesDto>> update(@RequestBody List<QuotesDto> quotesDtoList) {
        String txId = idWorker.nextId();
        List<QuotesDto> respList = quotesService.updateList(quotesDtoList, txId);
        Result<List<QuotesDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = quotesService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = quotesService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<QuotesDto>> search(@RequestBody SearchDto searchDto){
        List<QuotesDto> dtoList = quotesService.search(searchDto.getDataIds());
        Result<List<QuotesDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/RepairController.java
New file
@@ -0,0 +1,77 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.OrderDto;
import com.deloitte.system.request.RepairDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.service.RepairService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/repair")
public class RepairController {
    @Autowired
    RepairService repairService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<RepairDto> query(@RequestParam("dataId") String dataId) {
        RepairDto dto = repairService.queryForOne(dataId);
        Result<RepairDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<RepairDto>> insert(@RequestBody List<RepairDto> repairList) {
        String txId = idWorker.nextId();
        List<RepairDto> respList = repairService.insertList(repairList, txId);
        Result<List<RepairDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<RepairDto>> update(@RequestBody List<RepairDto> repairList) {
        String txId = idWorker.nextId();
        List<RepairDto> respList = repairService.updateList(repairList, txId);
        Result<List<RepairDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = repairService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = repairService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<RepairDto>> search(@RequestBody SearchDto searchDto){
        List<RepairDto> dtoList = repairService.search(searchDto.getDataIds());
        Result<List<RepairDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/SwoController.java
New file
@@ -0,0 +1,75 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.OrderDto;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.request.SwoDto;
import com.deloitte.system.service.SwoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/swo")
public class SwoController {
    @Autowired
    SwoService swoService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<SwoDto> query(@RequestParam("dataId") String dataId) {
        SwoDto dto = swoService.queryForOne(dataId);
        Result<SwoDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<SwoDto>> insert(@RequestBody List<SwoDto> swoDtoList) {
        String txId = idWorker.nextId();
        List<SwoDto> respList = swoService.insertList(swoDtoList, txId);
        Result<List<SwoDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<SwoDto>> update(@RequestBody List<SwoDto> swoDtoList) {
        String txId = idWorker.nextId();
        List<SwoDto> respList = swoService.updateList(swoDtoList, txId);
        Result<List<SwoDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = swoService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = swoService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<SwoDto>> search(@RequestBody SearchDto searchDto){
        List<SwoDto> dtoList = swoService.search(searchDto.getDataIds());
        Result<List<SwoDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/TSRepairController.java
New file
@@ -0,0 +1,76 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.request.SwoDto;
import com.deloitte.system.request.TSRepairDto;
import com.deloitte.system.service.TSRepairService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/tsrepair")
public class TSRepairController {
    @Autowired
    TSRepairService tsRepairService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<TSRepairDto> query(@RequestParam("dataId") String dataId) {
        TSRepairDto dto = tsRepairService.queryForOne(dataId);
        Result<TSRepairDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<TSRepairDto>> insert(@RequestBody List<TSRepairDto> tsRepairDtoList) {
        String txId = idWorker.nextId();
        List<TSRepairDto> respList = tsRepairService.insertList(tsRepairDtoList, txId);
        Result<List<TSRepairDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<TSRepairDto>> update(@RequestBody List<TSRepairDto> tsRepairDtoList) {
        String txId = idWorker.nextId();
        List<TSRepairDto> respList = tsRepairService.updateList(tsRepairDtoList, txId);
        Result<List<TSRepairDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = tsRepairService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = tsRepairService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<TSRepairDto>> search(@RequestBody SearchDto searchDto){
        List<TSRepairDto> dtoList = tsRepairService.search(searchDto.getDataIds());
        Result<List<TSRepairDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/TestController.java
New file
@@ -0,0 +1,24 @@
package com.deloitte.system.controller;
import com.common.annotation.NoLog;
import com.common.annotation.NoToken;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController{
    /**
     * 监控检测接口,勿动
     * */
    @NoLog
    @NoToken
    @RequestMapping(value = "/text4", method = RequestMethod.GET)
    public Result index4(){
        return Result.resp(ResultCodeEnum.RT_SUCCESS);
    }
}
src/main/java/com/deloitte/system/controller/TokenController.java
New file
@@ -0,0 +1,108 @@
package com.deloitte.system.controller;
import com.common.annotation.NoAuthorize;
import com.common.annotation.NoToken;
import com.common.annotation.RequestLimit;
import com.common.core.beans.Result;
import com.common.core.constant.GlobalConst;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.utils.DistributedLock;
import com.common.core.utils.JwtTokenUtil;
import com.common.redis.util.RedisUtil;
import com.common.security.configure.AppDetails;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Slf4j
@RestController
@RequestMapping("/token")
public class TokenController {
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private DistributedLock distributedLock;
    @NoToken
    @NoAuthorize
    @RequestLimit(count = -1)
    @RequestMapping(value = "/getToken", method = RequestMethod.GET)
    public Result<String> getToken(@RequestParam("app_id") String appId, @RequestParam("app_secret") String appSecret, @RequestParam(value = "user_id", required = false) String userid){
        String token = null;
        Result<String> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        String key="";
        try {
            if(StringUtils.isEmpty(appSecret)){
                throw new BadCredentialsException("appid, secret不正确");
            }
            AppDetails userDetails = (AppDetails)userDetailsService.loadUserByUsername(appId);
            String password = userDetails.getPassword();
            if (!appSecret.equals(password)) {
                throw new BadCredentialsException("appid, secret不正确");
            }
            if(StringUtils.isNotEmpty(userid)) {
                userDetails.setUserid(userid);
            }
            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            key=GlobalConst.TOKEN_KEY+appId;
            String keyTmp=GlobalConst.TOKEN_KEY_TMP+appId;
            if(StringUtils.isNotEmpty(userid)){
                key +=":"+userid;
                keyTmp+=":"+userid;
            }
            Object tokenCache=redisUtil.get(key);
            long expire=redisUtil.getExpire(key);
            if(tokenCache!=null && StringUtils.isNotEmpty(tokenCache.toString())&& expire>GlobalConst.TOKEN_EXPIRE_BUFF){
                token = tokenCache.toString();
            } else {
                try {
                    if (distributedLock.waitLock(key + ":lock", 300 * 1000)) {
                        tokenCache = redisUtil.get(key);
                        expire = redisUtil.getExpire(key);
                        if (tokenCache != null && StringUtils.isNotEmpty(tokenCache.toString()) && expire > GlobalConst.TOKEN_EXPIRE_BUFF) {
                            token = tokenCache.toString();
                        } else if (tokenCache != null && StringUtils.isNotEmpty(tokenCache.toString()) && expire <= GlobalConst.TOKEN_EXPIRE_BUFF) {
                            token = jwtTokenUtil.generateToken(userDetails);
                            redisUtil.set(key, token, GlobalConst.TOKEN_EXPIRE + GlobalConst.TOKEN_EXPIRE_BUFF);
                            redisUtil.set(keyTmp, tokenCache.toString(), expire);
                        } else {
                            token = jwtTokenUtil.generateToken(userDetails);
                            redisUtil.set(key, token, GlobalConst.TOKEN_EXPIRE + GlobalConst.TOKEN_EXPIRE_BUFF);
                        }
                    }
                } finally {
                    distributedLock.releaseLock(key+":lock");
                }
            }
        } catch (Exception e) {
            log.warn("登录异常:{}", e.getMessage());
            throw new BizException(ResultCodeEnum.LOGIN_FAILED);
        }
        res.setObject(token);
        return res;
    }
}
src/main/java/com/deloitte/system/controller/UserFaultInfoController.java
New file
@@ -0,0 +1,76 @@
package com.deloitte.system.controller;
import com.common.core.beans.Result;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.utils.IdUtils;
import com.deloitte.system.request.SearchDto;
import com.deloitte.system.request.TSRepairDto;
import com.deloitte.system.request.UserFaultInfoDto;
import com.deloitte.system.service.UserFaultInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/userfaultinfo")
public class UserFaultInfoController {
    @Autowired
    UserFaultInfoService userFaultInfoService;
    @Autowired
    private IdUtils idWorker;
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public Result<UserFaultInfoDto> query(@RequestParam("dataId") String dataId) {
        UserFaultInfoDto dto = userFaultInfoService.queryForOne(dataId);
        Result<UserFaultInfoDto> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dto);
        return res;
    }
    @RequestMapping(value = "/insert", method = RequestMethod.POST)
    public Result<List<UserFaultInfoDto>> insert(@RequestBody List<UserFaultInfoDto> userFaultInfoDtoList) {
        String txId = idWorker.nextId();
        List<UserFaultInfoDto> respList = userFaultInfoService.insertList(userFaultInfoDtoList, txId);
        Result<List<UserFaultInfoDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public Result<List<UserFaultInfoDto>> update(@RequestBody List<UserFaultInfoDto> userFaultInfoDtoList) {
        String txId = idWorker.nextId();
        List<UserFaultInfoDto> respList = userFaultInfoService.updateList(userFaultInfoDtoList, txId);
        Result<List<UserFaultInfoDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setTxId(txId);
        res.setObject(respList);
        return res;
    }
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Result<Boolean> delete(@RequestParam("dataId") String dataId) {
        Boolean flag = userFaultInfoService.deleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/undelete", method = RequestMethod.POST)
    public Result<Boolean> undelete(@RequestParam("dataId") String dataId) {
        Boolean flag = userFaultInfoService.undeleteOne(dataId);
        Result<Boolean> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(flag);
        return res;
    }
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public Result<List<UserFaultInfoDto>> search(@RequestBody SearchDto searchDto){
        List<UserFaultInfoDto> dtoList = userFaultInfoService.search(searchDto.getDataIds());
        Result<List<UserFaultInfoDto>> res = Result.resp(ResultCodeEnum.RT_SUCCESS);
        res.setObject(dtoList);
        return res;
    }
}
src/main/java/com/deloitte/system/job/CleanCacheTask.java
New file
@@ -0,0 +1,23 @@
package com.deloitte.system.job;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.beans.Introspector;
/**
 * @author 谢滨璜
 * @company deloitte
 * @date 2022-04-13
 * @descrition: 定时清理cache
 * */
@Slf4j
@Component
public class CleanCacheTask {
    @Scheduled(cron = "0 0 */1 * * ?")
    public void execute() {
        Introspector.flushCaches();
    }
}
src/main/java/com/deloitte/system/job/GuaranteedTask.java
New file
@@ -0,0 +1,353 @@
package com.deloitte.system.job;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.common.core.constant.GlobalConst;
import com.common.core.enums.TableNameEnum;
import com.common.core.enums.YesNoEnum;
import com.common.core.exception.BizException;
import com.common.core.service.SFService;
import com.common.core.utils.DistributedLock;
import com.common.core.utils.RestUtil;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.request.SFTokenDto;
import com.deloitte.system.request.TxConfirmDto;
import com.deloitte.system.service.ConfirmTxService;
import com.deloitte.system.service.FileService;
import com.jfinal.plugin.activerecord.Db;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author 廖振钦
 * @company deloitte
 * @date 2022-01-26
 * @descrition:
 *     兜底机制定时任务TODO
 * */
@Slf4j
@Component
public class GuaranteedTask {
    @Autowired
    private FileService fileService;
    @Autowired
    private SFService sfService;
    @Autowired
    private ConfirmTxService confirmTxService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private DistributedLock distributedLock;
    @Value("${salesforce.baseUrl}")
    private String baseUrl;
    /**
     * 每分钟跑一次
     */
    @Scheduled(cron = "0 * * * * ?")
    public void execute() {
        boolean isLock = false;
        boolean masterisLock = distributedLock.getLock("guaranteedLock-master",3600 * 1000);
        if(!masterisLock){
            isLock = distributedLock.getLock("guaranteedLock-slave",3600 * 1000);
        }
        if(masterisLock || isLock) {  //确保两台服务器都能同时跑又不会重复跑
            try {
                List<String> sets = new ArrayList<>();
                Set<String> insertsets = redisUtil.getKeys(GlobalConst.PIPL_UNCONFIRM_INSERT + "*");
                Set<String> updatesets = redisUtil.getKeys(GlobalConst.PIPL_UNCONFIRM_UPDATE + "*");
                sets.addAll(insertsets);
                sets.addAll(updatesets);
                Map<String, String> hashmap = new HashMap<>();
                for (String key : sets) {
                    String[] keys = key.split(":");
                    hashmap.put(keys[keys.length - 1], key);
                }
                List<String> sortkeysets = hashmap.keySet().stream().sorted(new Comparator<String>(){
                    @Override
                    public int compare(String o1, String o2) {
                        Long o1L = Long.parseLong(o1);
                        Long o2L = Long.parseLong(o2);
                        Long v = o2L-o1L; //倒序
//                        Long v = o1L-o2L; //顺序
                        if(v > 0){
                            return 1;
                        }else if(v < 0){
                            return -1;
                        }else {
                            return 0;
                        }
                    }
                }).limit(50).collect(Collectors.toList());
                if (CollUtil.isEmpty(sortkeysets)) {
                    return;
                }
                // 调用SF查询事务状态的接口,传入txId List,将查询的状态结果放在map,key为txId,value为处理状态
                Map<String, Map<String,String>> statusMap = queryTransactionStatus(sortkeysets);
//                Map<String, Map<String, String>> statusMap = query();
                for (String key : sortkeysets) {
                    boolean isExecute = distributedLock.getLock("guaranteed:"+key+":lock",3600 * 1000);//判断是否有机器在执行事务
                    //如果redis里面不存在这个key与没有机器在执行该事务确认,就执行确认事务,防止多台服务器重复跑
                    if((redisUtil.getKeys(GlobalConst.PIPL_UNCONFIRM_INSERT + "*").contains(GlobalConst.PIPL_UNCONFIRM_INSERT + key)
                    || redisUtil.getKeys(GlobalConst.PIPL_UNCONFIRM_UPDATE + "*").contains(GlobalConst.PIPL_UNCONFIRM_UPDATE + key))
                    && isExecute){
                        String txid = hashmap.get(key);
                        try {
                            // 获取该TXID的处理状态,如果成功,则确认事务,如果失败则删除key
                            List<String> collect = statusMap.keySet().stream().filter(i -> i.equals(key)).collect(Collectors.toList());
                            if (CollUtil.isNotEmpty(collect)) {
                                String tId = collect.get(0);
                                Map<String, String> stringStringMap = statusMap.get(tId);
                                String status = stringStringMap.get("status");
                                String sfRecordId = stringStringMap.get("sfRecordId");
                                if ("success".equals(status)) {
                                    TxConfirmDto dto=new TxConfirmDto(key,sfRecordId, YesNoEnum.YES.getCode(),null);
                                    confirmTxService.confirm(dto);
                                }else {
                                    redisUtil.deleteKey(txid);
                                }
                            }
                        } catch (BizException e) {
                            log.error(e.getMessage(), e);
                        }finally {
                            distributedLock.releaseLock("guaranteed:"+key+":lock");
                        }
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }finally {
                if(masterisLock){
                    distributedLock.releaseLock("guaranteedLock-master");
                }
                if (isLock){
                    distributedLock.releaseLock("guaranteedLock-slave");
                }
            }
        }
    }
    /**
     * 每分钟跑一次
     */
    @Scheduled(cron = "0 * * * * ?")
    public void executeFile() {
        boolean isLock = false;
        boolean masterisLock = distributedLock.getLock("guaranteedLock-master-file",3600 * 1000);
        if(!masterisLock){
            isLock = distributedLock.getLock("guaranteedLock-slave-file",3600 * 1000);
        }
        if(masterisLock || isLock) {  //确保两台服务器都能同时跑又不会重复跑
            try{
                List<String> sets = new ArrayList<>();
                Set<String> fileKeys = redisUtil.getKeys(GlobalConst.PIPL_UNCONFIRM_FILE + "*");
                sets.addAll(fileKeys);
                Map<String, String> hashmap = new HashMap<>();
                for (String key : sets) {
                    String[] keys = key.split(":");
                    hashmap.put(keys[keys.length - 1], key);
                }
                List<String> sortkeysets = hashmap.keySet().stream().sorted(new Comparator<String>(){
                    @Override
                    public int compare(String o1, String o2) {
                        Long o1L = Long.parseLong(o1);
                        Long o2L = Long.parseLong(o2);
                        Long v = o2L-o1L; //倒序
//                        Long v = o1L-o2L; //顺序
                        if(v > 0){
                            return 1;
                        }else if(v < 0){
                            return -1;
                        }else {
                            return 0;
                        }
                    }
                }).limit(50).collect(Collectors.toList());
                if (CollUtil.isEmpty(sortkeysets)) {
                    return;
                }
                Map<String, Map<String, String>> statusMap = queryTransactionStatus(sortkeysets);
//                Map<String, String> statusMap = query();
                for (String key : sortkeysets) {
                    boolean isExecute = distributedLock.getLock("guaranteed:"+key+":lock",3600 * 1000);//判断是否有机器在执行事务
                    //如果redis里面不存在这个key与没有机器在执行该事务确认,就执行确认事务,防止多台服务器重复跑
                    if((redisUtil.getKeys(GlobalConst.PIPL_UNCONFIRM_FILE + "*").contains(GlobalConst.PIPL_UNCONFIRM_FILE + key))
                            && isExecute) {
                        String txid = hashmap.get(key);
                        try {
                            //获取该TXID的处理状态,如果成功,则确认事务,如果失败则删除key
                            List<String> collect = statusMap.keySet().stream().filter(i -> i.equals(key)).collect(Collectors.toList());
                            if (CollUtil.isNotEmpty(collect)) {
                                String tId = collect.get(0);
                                Map<String, String> stringStringMap = statusMap.get(tId);
                                String status = stringStringMap.get("status");
                                String sfRecordId = stringStringMap.get("sfRecordId");
                                if ("success".equals(status)) {
//                                    confirmTxService.confirmFile(key);
                                    redisUtil.deleteKey(txid);
                                }else {
                                    List<String> keyList = redisUtil.getObjects(txid, String.class);
                                    try {
                                        fileService.cleanS3File(keyList);
                                    }catch (Exception e){
                                        log.error(e.getMessage(), e);
                                        throw new BizException("clean s3 file fail");
                                    }
                                    redisUtil.deleteKey(txid);
                                }
                            }
                        } catch (BizException e) {
                            log.error(e.getMessage(), e);
                        }finally {
                            distributedLock.releaseLock("guaranteed:"+key+":lock");
                        }
                    }
                }
            }catch (Exception e){
                log.error(e.getMessage(), e);
            }finally {
                if(masterisLock){
                    distributedLock.releaseLock("guaranteedLock-master-file");
                }
                if (isLock){
                    distributedLock.releaseLock("guaranteedLock-slave-file");
                }
            }
        }
    }
    /**
     * 同步表中的sfid(每天两点执行)
     */
    @Scheduled(cron = "0 0 2 * * ?")
    public void executeSyncSfid() {
        boolean isLock = false;
        boolean masterisLock = distributedLock.getLock("guaranteedLock-syncSfid-master",3600 * 1000);
        if(!masterisLock){
            isLock = distributedLock.getLock("guaranteedLock-syncSfid-slave",3600 * 1000);
        }
        if(masterisLock || isLock) {  //确保两台服务器都能同时跑又不会重复跑
            ArrayList<String> lockKeys = new ArrayList<>();
            try {
                //aws tableName与sf tableName的映射
                List<String> allAwsTableName = TableNameEnum.getAllAwsTableName();
                for (String awsTableName:allAwsTableName) {
                    List<Long> ids = Db.query("select id from `"+awsTableName+"` where is_delete = '0' and create_time>date_add(now(),interval -2 MONTH) and sf_record_id is null");
                    log.info("同步表{},查询到sf_id为空的数据共{}条",awsTableName,ids.size());
                    if (CollUtil.isEmpty(ids)) {
                        continue;
                    }
                    //加表名锁,阻止多个服务同时更新一张表
                    //判断是否有机器在执行事务
                    boolean isExecute = distributedLock.getLock("guaranteed-syncSfid:"+awsTableName+":lock",3600 * 1000);
                    if(isExecute) {
                        lockKeys.add(awsTableName);
                        String sfTableName = TableNameEnum.getSfTableNameByAwsTableName(awsTableName);
                        //截取ids每50条执行一次
                        int skip = 0;
                        int sub = 50;
                        List<String> collect = null;
                        String idColumn = "AWS_Data_Id__c";
                        while (collect==null||collect.size()>=sub) {
                            collect = ids.stream().skip(skip).limit(sub).map(String::valueOf).collect(Collectors.toList());
                            skip += sub;
                            String sql = "select Id,"+idColumn+"  from "+sfTableName+" where "+idColumn+" in "+collect.stream().collect(Collectors.joining("','", "('", "')"));
                            JSONArray jsonArray = sfService.querySFData(sql);
                            if (jsonArray != null && jsonArray.size() != 0) {
                                for (int i = 0;i<jsonArray.size();i++) {
                                    JSONObject data = jsonArray.getJSONObject(i);
                                    String sfId = data.getString("Id");
                                    String awsId = data.getString(idColumn);
                                    Db.update("update `"+awsTableName+"` set sf_record_id = '"+ sfId +"' where id = "+awsId);
                                    log.info("更新{} sfid,id:{},sfid:{}",awsTableName,awsId,sfId);
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.error("同步contact sfid异常:"+e.getMessage(), e);
            }finally {
                if(masterisLock){
                    distributedLock.releaseLock("guaranteedLock-syncSfid-master");
                }
                if (isLock){
                    distributedLock.releaseLock("guaranteedLock-syncSfid-slave");
                }
                //释放数据锁
                if(CollUtil.isNotEmpty(lockKeys)){
                    for (String awsTableName:lockKeys) {
                        distributedLock.releaseLock("guaranteed-syncSfid:"+awsTableName+":lock");
                    }
                }
            }
        }
    }
    public Map<String,Map<String,String>> queryTransactionStatus(List<String> sortkeysets){
        SFTokenDto tokenDto = sfService.getToken();
        String accessToken = tokenDto.getAccessToken();
        HttpHeaders header = new HttpHeaders();
        header.add("Authorization", "Bearer " + accessToken);
        StringBuilder idString = new StringBuilder();
        // 拼接URL
        String ids = sortkeysets.stream().collect(Collectors.joining("','", "('", "')"));
        idString.append(baseUrl).append("services/data/v53.0/query/?q=Select Status__c,TransId__c,SFRecordId__c from Transaction_Log__c Where TransId__c in ").append(ids);
        // 请求数据
        JSONObject jsonObject = RestUtil.get(idString.toString(),header);
        Map<String, Map<String,String>> map = new HashMap<>();
        List<JSONObject> objects = new ArrayList<>();
        Object totalSize = jsonObject.get("totalSize");
        Boolean done =(Boolean) jsonObject.get("done");
        String nextRecordsUrl = jsonObject.getString("nextRecordsUrl");
        JSONArray jsonArray = jsonObject.getJSONArray("records");
        if (totalSize == null) {
            log.info("请求事务确认数据失败,result:{}",jsonObject);
            throw new BizException("请求事务确认数据失败");
        }
        for (int i = 0; i <jsonArray.size() ; i++) {
            JSONObject data = jsonArray.getJSONObject(i);
            String status = data.getString("Status__c");
            String transId = data.getString("TransId__c");
            String sfRecordId = data.getString("SFRecordId__c");
            Map<String, String> mapStatus = new HashMap<>();
            mapStatus.put("status",status);
            mapStatus.put("sfRecordId",sfRecordId);
            map.put(transId,mapStatus);
        }
        return map;
    }
    public Map<String, String> queryNextRecords(String nextRecordsUrl,Map<String,String> map){
        SFTokenDto tokenDto = sfService.getToken();
        String accessToken = tokenDto.getAccessToken();
        HttpHeaders header = new HttpHeaders();
        header.add("Authorization", "Bearer " + accessToken);
        JSONObject jsonObject = RestUtil.get(nextRecordsUrl,header);
        Boolean done =(Boolean) jsonObject.get("done");
        JSONArray jsonArray = jsonObject.getJSONArray("records");
        Object totalSize = jsonObject.get("totalSize");
        if (totalSize == null) {
            return map;
        }
        for (int i = 0; i <jsonArray.size() ; i++) {
            JSONObject data = jsonArray.getJSONObject(i);
            String status = data.getString("Status__c");
            String transId = data.getString("TransId__c");
            map.put(transId,status);
        }
        return map;
    }
}
src/main/java/com/deloitte/system/job/MailSyncTask.java
New file
@@ -0,0 +1,38 @@
package com.deloitte.system.job;
import com.common.core.utils.DistributedLock;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
 *  邮件同步任务
 */
@Slf4j
@Component
public class MailSyncTask {
    @Autowired
    private MailService mailService;
    @Autowired
    private DistributedLock distributedLock;
    @Scheduled(cron = "1 * * * * ?")
    public void execute() {
        boolean isLock = false;
        isLock = distributedLock.getLock("mailSyncLock",3600 * 1000);
        if(isLock) {
            try {
                mailService.receiveImapMail();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }finally {
                distributedLock.releaseLock("mailSyncLock");
            }
        }
    }
}
src/main/java/com/deloitte/system/model/Account.java
New file
@@ -0,0 +1,55 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
/**
 * @Author: Ben Pi
 * @Date: 22/02/2022 15:59
 */
@Table(tableName = "account", clazz = Account.class)
public class Account extends BaseModel<Account> {
    public static final Account dao = new Account();
    public Long getDataId() {
        return getLong("id");
    }
    public Account setDataId(Long dataId) {
        set("id", dataId);
        return this;
    }
    public String getMobilePhoneNumber(){
        return getStr("mobile_phone_number");
    }
    public Account setMobilePhoneNumber(String mobilePhoneNumber){
        set("mobile_phone_number", mobilePhoneNumber);
        return this;
    }
    public String getPhoneD(){
        return getStr("phone_d");
    }
    public Account setPhoneD(String phoneD){
        set("phone_d", phoneD);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public Account setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public Account findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/AppConfig.java
New file
@@ -0,0 +1,37 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Table(tableName = "app_config", clazz = AppConfig.class)
public class AppConfig extends BaseModel<AppConfig> {
    public static final AppConfig dao = new AppConfig();
    public String appid(){
        return getStr("app_id");
    }
    public AppConfig appid(String appid){
        set("app_id", appid);
        return this;
    }
    public String appsecret(){
        return getStr("app_secret");
    }
    public AppConfig appsecret(String appsecret){
        set("app_secret", appsecret);
        return this;
    }
    public AppConfig findByAppid(String appid){
        return this.dao.findFirst("select * from " + getTableName() + " where app_id=? ",appid);
    }
}
src/main/java/com/deloitte/system/model/AppPermissionConfig.java
New file
@@ -0,0 +1,39 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
import java.util.List;
/**
 * @author 廖振钦
 * @date 2022-01-17
 */
@Table(tableName = "app_permission_config", clazz = AppPermissionConfig.class)
public class AppPermissionConfig extends BaseModel<AppPermissionConfig> {
    public static final AppPermissionConfig dao = new AppPermissionConfig();
    public String appid(){
        return getStr("app_id");
    }
    public AppPermissionConfig appid(String appid){
        set("app_id", appid);
        return this;
    }
    public String url(){
        return getStr("url");
    }
    public AppPermissionConfig url(String url){
        set("url", url);
        return this;
    }
    public List<AppPermissionConfig> findByAppidList(String appid){
        return this.dao.find("select * from "+ getTableName() + " where app_id=?",appid);
    }
}
src/main/java/com/deloitte/system/model/CacheList.java
New file
@@ -0,0 +1,21 @@
package com.deloitte.system.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
 * @Author: Ben Pi
 * @Date: 24/01/2022 15:58
 * @Description:   用于存入的List,带有表名属性
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CacheList<V>{
    private List<V> data;
    private String tableName;
}
src/main/java/com/deloitte/system/model/CampaignUser.java
New file
@@ -0,0 +1,65 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
import java.util.List;
@Table(tableName = "campaign_user", clazz = CampaignUser.class)
public class CampaignUser extends BaseModel<CampaignUser> {
    public static final CampaignUser dao=new CampaignUser();
    public Long getDataId(){
        return getLong("id");
    }
    public CampaignUser setDataId(Long dataId) {
        set("id", dataId);
        return this;
    }
    public String getName(){
        return getStr("name");
    }
    public CampaignUser setName(String name){
        set("name", name);
        return this;
    }
    public String getPhone(){
        return getStr("phone");
    }
    public CampaignUser setPhone(String phone){
        set("phone", phone);
        return this;
    }
    public String getEmail(){
        return getStr("email");
    }
    public CampaignUser setEmail(String email){
        set("email", email);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public CampaignUser setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public CampaignUser findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
    public List<CampaignUser> findListBySql(String sql){
        return this.dao.find(sql);
    }
}
src/main/java/com/deloitte/system/model/Contact.java
New file
@@ -0,0 +1,258 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "contact", clazz = Contact.class)
public class Contact extends BaseModel<Contact> {
    public static final Contact dao = new Contact();
    public Long getDataId(){
        return getLong("id");
    }
    public Contact setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getFirstName(){
        return getStr("first_name");
    }
    public Contact setFirstName(String firstName){
        set("first_name", firstName);
        return this;
    }
    public String getLastName(){
        return getStr("last_name");
    }
    public Contact setLastName(String lastName){
        set("last_name", lastName);
        return this;
    }
    public String getPostcode(){
        return getStr("postcode");
    }
    public Contact setPostcode(String postcode){
        set("postcode", postcode);
        return this;
    }
    public String getPostcodeD(){
        return getStr("postcode_d");
    }
    public Contact setPostcodeD(String postcodeD){
        set("postcode_d", postcodeD);
        return this;
    }
    public String getTitleD(){
        return getStr("title_d");
    }
    public Contact setTitleD(String titleD){
        set("title_d", titleD);
        return this;
    }
    public String getTitle(){
        return getStr("title");
    }
    public Contact setTitle(String title){
        set("title", title);
        return this;
    }
    public String getContactEnglishName(){
        return getStr("contact_english_name");
    }
    public Contact setContactEnglishName(String contactEnglishName){
        set("contact_english_name", contactEnglishName);
        return this;
    }
    public String getEnglishAddress(){
        return getStr("english_address");
    }
    public Contact setEnglishAddress(String englishAddress){
        set("english_address", englishAddress);
        return this;
    }
    public String getAddress1(){
        return getStr("address1");
    }
    public Contact setAddress1(String address1){
        set("address1", address1);
        return this;
    }
    public String getAddress2(){
        return getStr("address2");
    }
    public Contact setAddress2(String address2){
        set("address2", address2);
        return this;
    }
    public String getAddress3(){
        return getStr("address3");
    }
    public Contact setAddress3(String address3){
        set("address3", address3);
        return this;
    }
    public String getAddress1D(){
        return getStr("address1_d");
    }
    public Contact setAddress1D(String address1D){
        set("address1_d", address1D);
        return this;
    }
    public String getAddress2D(){
        return getStr("address2_d");
    }
    public Contact setAddress2D(String address2D){
        set("address2_d", address2D);
        return this;
    }
    public String getAddress3D(){
        return getStr("address3_d");
    }
    public Contact setAddress3D(String address3D){
        set("address3_d", address3D);
        return this;
    }
    public String getFax(){
        return getStr("fax");
    }
    public Contact setFax(String fax){
        set("fax", fax);
        return this;
    }
    public String getFaxD(){
        return getStr("fax_d");
    }
    public Contact setFaxD(String faxD){
        set("fax_d", faxD);
        return this;
    }
    public String getEmailD(){
        return getStr("email_d");
    }
    public Contact setEmailD(String emailD){
        set("email_d", emailD);
        return this;
    }
    public String getEmail(){
        return getStr("email");
    }
    public Contact setEmail(String email){
        set("email", email);
        return this;
    }
    public String getMobilePhoneD(){
        return getStr("mobile_phone_d");
    }
    public Contact setMobilePhoneD(String mobilePhoneD){
        set("mobile_phone_d", mobilePhoneD);
        return this;
    }
    public String getOtherPhoneD(){
        return getStr("other_phone_d");
    }
    public Contact setOtherPhoneD(String otherPhoneD){
        set("other_phone_d", otherPhoneD);
        return this;
    }
    public String getPhoneD(){
        return getStr("phone_d");
    }
    public Contact setPhoneD(String phoneD){
        set("phone_d", phoneD);
        return this;
    }
    public String getHomePhone(){
        return getStr("home_phone");
    }
    public Contact setHomePhone(String homePhone){
        set("home_phone", homePhone);
        return this;
    }
    public String getMobilePhone(){
        return getStr("mobile_phone");
    }
    public Contact setMobilePhone(String mobilePhone){
        set("mobile_phone", mobilePhone);
        return this;
    }
    public String getOtherPhone(){
        return getStr("other_phone");
    }
    public Contact setOtherPhone(String otherPhone){
        set("other_phone", otherPhone);
        return this;
    }
    public String getPhone(){
        return getStr("phone");
    }
    public Contact setPhone(String Phone){
        set("phone", Phone);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public Contact setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public Contact findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/LoanerApplication.java
New file
@@ -0,0 +1,122 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "loaner_application", clazz = LoanerApplication.class)
public class LoanerApplication extends BaseModel<LoanerApplication> {
    public static final LoanerApplication dao = new LoanerApplication();
    public Long getDataId(){
        return getLong("id");
    }
    public LoanerApplication setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getApplyPerson(){
        return getStr("apply_person");
    }
    public LoanerApplication setApplyPerson(String applyPerson){
        set("apply_person", applyPerson);
        return this;
    }
    public String getApplyPersonPhone(){
        return getStr("apply_person_phone");
    }
    public LoanerApplication setApplyPersonPhone(String applyPersonPhone){
        set("apply_person_phone", applyPersonPhone);
        return this;
    }
    public String getApplicantMailbox(){
        return getStr("applicant_mailbox");
    }
    public LoanerApplication setApplicantMailbox(String applicantMailbox){
        set("applicant_mailbox", applicantMailbox);
        return this;
    }
    public String getLoanerReceiveStaff(){
        return getStr("loaner_receive_staff");
    }
    public LoanerApplication setLoanerReceiveStaff(String loanerReceiveStaff){
        set("loaner_receive_staff", loanerReceiveStaff);
        return this;
    }
    public String getDirectShippmentAddress(){
        return getStr("direct_shippment_address");
    }
    public LoanerApplication setdirectShippmentAddress(String directShippmentAddress){
        set("direct_shippment_address", directShippmentAddress);
        return this;
    }
    public String getLoanerReceiveStaffPhone(){
        return getStr("loaner_receive_staff_phone");
    }
    public LoanerApplication setLoanerReceiveStaffPhone(String loanerReceiveStaffPhone){
        set("loaner_receive_staff_phone", loanerReceiveStaffPhone);
        return this;
    }
    public String getReturnTrakeStaff(){
        return getStr("return_trake_staff");
    }
    public LoanerApplication setReturnTrakeStaff(String returnTrakeStaff){
        set("return_trake_staff", returnTrakeStaff);
        return this;
    }
    public String getReturnNumber(){
        return getStr("return_number");
    }
    public LoanerApplication setReturnNumber(String returnNumber){
        set("return_number", returnNumber);
        return this;
    }
    public String getLoanerSer(){
        return getStr("loaner_ser");
    }
    public LoanerApplication setLoanerSer(String loanerSer){
        set("loaner_ser", loanerSer);
        return this;
    }
    public String getPostCode(){
        return getStr("post_code");
    }
    public LoanerApplication setPostCode(String postCode){
        set("post_code", postCode);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public LoanerApplication setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public LoanerApplication findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/LoanerUser.java
New file
@@ -0,0 +1,46 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "loaner_user",clazz = LoanerUser.class)
public class LoanerUser extends BaseModel<LoanerUser> {
    public static final LoanerUser dao =new LoanerUser();
    public Long getDataId(){
        return getLong("id");
    }
    public LoanerUser setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getContact(){
        return getStr("contact");
    }
    public LoanerUser setContact(String contact){
        set("contact",contact);
        return this;
    }
    public String getContactNumber(){
        return getStr("contact_number");
    }
    public LoanerUser setContactNumber(String contactNumber){
        set("contact_number",contactNumber);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public LoanerUser setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public LoanerUser findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/MailMerge.java
New file
@@ -0,0 +1,340 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
import java.util.Date;
@Table(tableName = "mail_merge",clazz = MailMerge.class)
public class MailMerge extends BaseModel<MailMerge> {
    public static final MailMerge dao =new MailMerge();
    public Long getDataId(){
        return getLong("id");
    }
    public MailMerge setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public MailMerge setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public String getName() {
        return getStr("name");
    }
    public MailMerge setName(String name) {
        set("name", name);
        return this;
    }
    public String getCc() {
        return getStr("cc");
    }
    public MailMerge setCc(String cc) {
        set("cc", cc);
        return this;
    }
    public String getBcc() {
        return getStr("bcc");
    }
    public MailMerge setBcc(String bcc) {
        set("bcc", bcc);
        return this;
    }
    public String getAuthor() {
        return getStr("author");
    }
    public MailMerge setAuthor(String author) {
        set("author", author);
        return this;
    }
    public String getAllMember() {
        return getStr("all_member");
    }
    public MailMerge setAllMember(String allMember) {
        set("all_member", allMember);
        return this;
    }
    public String getAllMemberType() {
        return getStr("all_member_type");
    }
    public MailMerge setAllMemberType(String allMemberType) {
        set("all_member_type", allMemberType);
        return this;
    }
    public String getToName() {
        return getStr("to_name");
    }
    public MailMerge setToName(String toName) {
        set("to_name", toName);
        return this;
    }
    public String getAllMemberName() {
        return getStr("all_member_name");
    }
    public MailMerge setAllMemberName(String allMemberName) {
        set("all_member_name", allMemberName);
        return this;
    }
    public String getMailType() {
        return getStr("mail_type");
    }
    public MailMerge setMailType(String mailType) {
        set("mail_type", mailType);
        return this;
    }
    public String getCcName() {
        return getStr("cc_name");
    }
    public MailMerge setCcName(String ccName) {
        set("cc_name", ccName);
        return this;
    }
    public String getBccName() {
        return getStr("bcc_name");
    }
    public MailMerge setBccName(String bccName) {
        set("bcc_name", bccName);
        return this;
    }
    public String getType() {
        return getStr("type");
    }
    public MailMerge setType(String type) {
        set("type", type);
        return this;
    }
    public String getSubject() {
        return getStr("subject");
    }
    public MailMerge setSubject(String subject) {
        set("subject", subject);
        return this;
    }
    public String getSubjectCopy() {
        return getStr("subject_copy");
    }
    public MailMerge setSubjectCopy(String subjectCopy) {
        set("subject_copy", subjectCopy);
        return this;
    }
    public String getRecord() {
        return getStr("record");
    }
    public MailMerge setRecord(String record) {
        set("record", record);
        return this;
    }
    public String getRecordType() {
        return getStr("record_type");
    }
    public MailMerge setRecordType(String recordType) {
        set("record_type", recordType);
        return this;
    }
    public String getRecipient() {
        return getStr("recipient");
    }
    public MailMerge setRecipient(String recipient) {
        set("recipient", recipient);
        return this;
    }
    public String getPremaryRecipient() {
        return getStr("premary_recipient");
    }
    public MailMerge setPremaryRecipient(String premaryRecipient) {
        set("premary_recipient", premaryRecipient);
        return this;
    }
    public String getFiles() {
        return getStr("files");
    }
    public MailMerge setFiles(String files) {
        set("files", files);
        return this;
    }
    public String getEmalSent() {
        return getStr("emal_sent");
    }
    public MailMerge setEmalSent(String emalSent) {
        set("emal_sent", emalSent);
        return this;
    }
    public String getEmailSent() {
        return getStr("email_sent");
    }
    public MailMerge setEmailSent(String emailSent) {
        set("email_sent", emailSent);
        return this;
    }
    public String getCurrencyIsoCode() {
        return getStr("currency_iso_code");
    }
    public MailMerge setCurrencyIsoCode(String currencyIsoCode) {
        set("currency_iso_code", currencyIsoCode);
        return this;
    }
    public String getInternalOnly() {
        return getStr("internal_only");
    }
    public MailMerge setInternalOnly(String internalOnly) {
        set("internal_only", internalOnly);
        return this;
    }
    public String getOwnerId() {
        return getStr("owner_id");
    }
    public MailMerge setOwnerId(String ownerId) {
        set("owner_id", ownerId);
        return this;
    }
    public String getLastModifiedById() {
        return getStr("last_modified_by_id");
    }
    public MailMerge setLastModifiedById(String lastModifiedById) {
        set("last_modified_by_id", lastModifiedById);
        return this;
    }
    public String getCreatedById() {
        return getStr("created_by_id");
    }
    public MailMerge setCreatedById(String createdById) {
        set("created_by_id", createdById);
        return this;
    }
    public String getSwo() {
        return getStr("swo");
    }
    public MailMerge setSwo(String swo) {
        set("swo", swo);
        return this;
    }
    public String getQuotes() {
        return getStr("quotes");
    }
    public MailMerge setQuotes(String quotes) {
        set("quotes", quotes);
        return this;
    }
    public String getSend() {
        return getStr("send");
    }
    public MailMerge setSend(String send) {
        set("send", send);
        return this;
    }
    public String getCaseF() {
        return getStr("case_f");
    }
    public MailMerge setCaseF(String caseF) {
        set("case_f", caseF);
        return this;
    }
    public String getFrom() {
        return getStr("from");
    }
    public MailMerge setFrom(String from) {
        set("from", from);
        return this;
    }
    public Date getDate() {
        return getDate("date");
    }
    public MailMerge setDate(Date date) {
        set("date", date);
        return this;
    }
    public String getMessage() {
        return getStr("message");
    }
    public MailMerge setMessage(String message) {
        set("message", message);
        return this;
    }
    public String getSfFileAddressId() {
        return getStr("sf_file_address_id");
    }
    public MailMerge setSfFileAddressId(String sfFileAddressId) {
        set("sf_file_address_id", sfFileAddressId);
        return this;
    }
    public MailMerge findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/Opportunity.java
New file
@@ -0,0 +1,98 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "opportunity", clazz = Opportunity.class)
public class Opportunity extends BaseModel<Opportunity> {
    public static final Opportunity dao=new Opportunity();
    public Long getDataId(){
        return getLong("id");
    }
    public Opportunity setDataId(Long dataId) {
        set("id", dataId);
        return this;
    }
    public String getDealerServiceD(){
        return getStr("dealer_service_d");
    }
    public Opportunity setDealerServiceD(String dealerServiceD){
        set("dealer_service_d", dealerServiceD);
        return this;
    }
    public String getDealerService(){
        return getStr("dealer_service");
    }
    public Opportunity setDealerService(String dealerService){
        set("dealer_service", dealerService);
        return this;
    }
    public String getDealerSalesStaffNameD(){
        return getStr("dealer_sales_staff_name_d");
    }
    public Opportunity setDealerSalesStaffNameD(String dealerSalesStaffNameD){
        set("dealer_sales_staff_name_d", dealerSalesStaffNameD);
        return this;
    }
    public String getDealerSalesStaffName(){
        return getStr("dealer_sales_staff_name");
    }
    public Opportunity setDealerSalesStaffName(String dealerSalesStaffName){
        set("dealer_sales_staff_name", dealerSalesStaffName);
        return this;
    }
    public String getExpectedDeliveryDate(){
        return getStr("expected_delivery_date");
    }
    public Opportunity setExpectedDeliveryDate(String expectedDeliveryDate){
        set("expected_delivery_date", expectedDeliveryDate);
        return this;
    }
    public String getSalesAccountCode(){
        return getStr("sales_account_code");
    }
    public Opportunity setSalesAccountCode(String salesAccountCode){
        set("sales_account_code", salesAccountCode);
        return this;
    }
    public String getSpecialDeliveryAddress(){
        return getStr("special_delivery_address");
    }
    public Opportunity setSpecialDeliveryAddress(String specialDeliveryAddress){
        set("special_delivery_address", specialDeliveryAddress);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public Opportunity setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public Opportunity findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/Order.java
New file
@@ -0,0 +1,402 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "order", clazz = Order.class)
public class Order extends BaseModel<Order> {
    public static final Order dao = new Order();
    public Long getDataId(){
        return getLong("id");
    }
    public Order setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getSpecialDeliveryPhone(){
        return getStr("special_delivery_phone");
    }
    public Order setSpecialDeliveryPhone(String specialDeliveryPhone){
        set("special_delivery_phone", specialDeliveryPhone);
        return this;
    }
    public String getSpecialDeliveryPhoneD(){
        return getStr("special_delivery_phone_d");
    }
    public Order setSpecialDeliveryPhoneD(String specialDeliveryPhoneD){
        set("special_delivery_phone_d", specialDeliveryPhoneD);
        return this;
    }
    public String getShippingRecieverEmailAdr(){
        return getStr("shipping_reciever_email_adr");
    }
    public Order setShippingRecieverEmailAdr(String shippingRecieverEmailAdr){
        set("shipping_reciever_email_adr", shippingRecieverEmailAdr);
        return this;
    }
    public String getShipToContactId(){
        return getStr("ship_to_contact_id");
    }
    public Order setShipToContactId(String shipToContactId){
        set("ship_to_contact_id", shipToContactId);
        return this;
    }
    public String getCustomerAuthorizedById(){
        return getStr("customer_authorized_by_id");
    }
    public Order setCustomerAuthorizedById(String customerAuthorizedById){
        set("customer_authorized_by_id", customerAuthorizedById);
        return this;
    }
    public String getBillToContactId(){
        return getStr("bill_to_contact_id");
    }
    public Order setBillToContactId(String billToContactId){
        set("bill_to_contact_id", billToContactId);
        return this;
    }
    public String getSpecialDeliveryContact2(){
        return getStr("special_delivery_contact2");
    }
    public Order setSpecialDeliveryContact2(String specialDeliveryContact2){
        set("special_delivery_contact2", specialDeliveryContact2);
        return this;
    }
    public String getSpecialDeliveryContactText(){
        return getStr("special_delivery_contact_text");
    }
    public Order setSpecialDeliveryContactText(String specialDeliveryContactText){
        set("special_delivery_contact_text", specialDeliveryContactText);
        return this;
    }
    public String getShippingAddressText(){
        return getStr("shipping_address_text");
    }
    public Order setShippingAddressText(String shippingAddressText){
        set("shipping_address_text", shippingAddressText);
        return this;
    }
    public String getSpecialDeliveryContact2D(){
        return getStr("special_delivery_contact2_d");
    }
    public Order setSpecialDeliveryContact2D(String specialDeliveryContact2D){
        set("special_delivery_contact2_d", specialDeliveryContact2D);
        return this;
    }
    public String getEndUser(){
        return getStr("end_user");
    }
    public Order setEndUser(String endUser){
        set("end_user", endUser);
        return this;
    }
    public String getEndUserD(){
        return getStr("end_user_d");
    }
    public Order setEndUserD(String endUserD){
        set("end_user_d", endUserD);
        return this;
    }
    public String getSpecialDeliveryContact(){
        return getStr("special_delivery_contact");
    }
    public Order setSpecialDeliveryContact(String specialDeliveryContact){
        set("special_delivery_contact", specialDeliveryContact);
        return this;
    }
    public String getSpecialDeliveryContactD(){
        return getStr("special_delivery_contact_d");
    }
    public Order setSpecialDeliveryContactD(String specialDeliveryContactD){
        set("special_delivery_contact_d", specialDeliveryContactD);
        return this;
    }
    public String getPdfNNotifyParty(){
        return getStr("pdf_n_notify_party");
    }
    public Order setPdfNNotifyParty(String pdfNNotifyParty){
        set("pdf_n_notify_party", pdfNNotifyParty);
        return this;
    }
    public String getPdfNFax(){
        return getStr("pdf_n_fax");
    }
    public Order setPdfNFax(String pdfNFax){
        set("pdf_n_fax", pdfNFax);
        return this;
    }
    public String getPdfNContact(){
        return getStr("pdf_n_contact");
    }
    public Order setPdfNContact(String pdfNContact){
        set("pdf_n_contact", pdfNContact);
        return this;
    }
    public String getPdfSTel(){
        return getStr("pdf_s_tel");
    }
    public Order setPdfSTel(String pdfSTel){
        set("pdf_s_tel", pdfSTel);
        return this;
    }
    public String getPdfSPhone(){
        return getStr("pdf_s_phone");
    }
    public Order setPdfSPhone(String pdfSPhone){
        set("pdf_s_phone", pdfSPhone);
        return this;
    }
    public String getPdfSName(){
        return getStr("pdf_s_name");
    }
    public Order setPdfSName(String pdfSName){
        set("pdf_s_name", pdfSName);
        return this;
    }
    public String getPdfSFax(){
        return getStr("pdf_s_fax");
    }
    public Order setPdfSFax(String pdfSFax){
        set("pdf_s_fax", pdfSFax);
        return this;
    }
    public String getPdfFTel(){
        return getStr("pdf_f_tel");
    }
    public Order setPdfFTel(String pdfFTel){
        set("pdf_f_tel", pdfFTel);
        return this;
    }
    public String getPdfFFax(){
        return getStr("pdf_f_fax");
    }
    public Order setPdfFFax(String pdfFFax){
        set("pdf_f_fax", pdfFFax);
        return this;
    }
    public String getPdfFContactPerson(){
        return getStr("pdf_f_contact_person");
    }
    public Order setPdfFContactPerson(String pdfFContactPerson){
        set("pdf_f_contact_person", pdfFContactPerson);
        return this;
    }
    public String getPdfSAdds(){
        return getStr("pdf_s_adds");
    }
    public Order setPdfSAdds(String pdfSAdds){
        set("pdf_s_adds", pdfSAdds);
        return this;
    }
    public String getPdfSAddress(){
        return getStr("pdf_s_address");
    }
    public Order setPdfSAddress(String pdfSAddress){
        set("pdf_s_address", pdfSAddress);
        return this;
    }
    public String getPdfSeller(){
        return getStr("pdf_seller");
    }
    public Order setPdfSeller(String pdfSeller){
        set("pdf_seller", pdfSeller);
        return this;
    }
    public String getPdfCTheconsigne(){
        return getStr("pdf_c_theconsigne");
    }
    public Order setPdfCTheconsigne(String pdfCTheconsigne){
        set("pdf_c_theconsigne", pdfCTheconsigne);
        return this;
    }
    public String getPdfCTel(){
        return getStr("pdf_c_tel");
    }
    public Order setPdfCTel(String pdfCTel){
        set("pdf_c_tel", pdfCTel);
        return this;
    }
    public String getPdfCFax(){
        return getStr("pdf_c_fax");
    }
    public Order setPdfCFax(String pdfCFax){
        set("pdf_c_fax", pdfCFax);
        return this;
    }
    public String getPdfCConsignee(){
        return getStr("pdf_c_consignee");
    }
    public Order setPdfCConsignee(String pdfCConsignee){
        set("pdf_c_consignee", pdfCConsignee);
        return this;
    }
    public String getPdfCContact(){
        return getStr("pdf_c_contact");
    }
    public Order setPdfCContact(String pdfCContact){
        set("pdf_c_contact", pdfCContact);
        return this;
    }
    public String getPdfCAddr(){
        return getStr("pdf_c_addr");
    }
    public Order setPdfCAddr(String pdfCAddr){
        set("pdf_c_addr", pdfCAddr);
        return this;
    }
    public String getSpecialDeliveryAddress(){
        return getStr("special_delivery_address");
    }
    public Order setSpecialDeliveryAddress(String specialDeliveryAddress){
        set("special_delivery_address", specialDeliveryAddress);
        return this;
    }
    public String getSpecialDeliveryAddressD(){
        return getStr("special_delivery_address_d");
    }
    public Order setSpecialDeliveryAddressD(String specialDeliveryAddressD){
        set("special_delivery_address_d", specialDeliveryAddressD);
        return this;
    }
    public String getDealerSalesStaffNameA(){
        return getStr("dealer_sales_staff_name_a");
    }
    public Order setDealerSalesStaffNameA(String dealerSalesStaffNameA){
        set("dealer_sales_staff_name_a", dealerSalesStaffNameA);
        return this;
    }
    public String getPdfSignTitle(){
        return getStr("pdf_sign_title");
    }
    public Order setPdfSignTitle(String pdfSignTitle){
        set("pdf_sign_title", pdfSignTitle);
        return this;
    }
    public String getPdfSignName(){
        return getStr("pdf_sign_name");
    }
    public Order setPdfSignName(String pdfSignName){
        set("pdf_sign_name", pdfSignName);
        return this;
    }
    public String getPdfSignaturePlaces(){
        return getStr("pdf_signature_places");
    }
    public Order setPdfSignaturePlaces(String pdfSignaturePlaces){
        set("pdf_signature_places", pdfSignaturePlaces);
        return this;
    }
    public String getPdfByTel(){
        return getStr("pdf_by_tel");
    }
    public Order setPdfByTel(String pdfByTel){
        set("pdf_by_tel", pdfByTel);
        return this;
    }
    public String getPdfByAdd(){
        return getStr("pdf_by_add");
    }
    public Order setPdfByAdd(String pdfByAdd){
        set("pdf_by_add", pdfByAdd);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public Order setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public Order findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/Quotes.java
New file
@@ -0,0 +1,90 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "quotes",clazz = Quotes.class)
public class Quotes extends BaseModel<Quotes> {
    public static final Quotes dao =new Quotes();
    public Long getDataId(){
        return getLong("id");
    }
    public Quotes setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getContactName(){
        return getStr("contact_name");
    }
    public Quotes setContactName(String contactName){
        set("contact_name",contactName);
        return this;
    }
    public String getContactPhone(){
        return getStr("contact_phone");
    }
    public Quotes setContactPhone(String contactPhone){
        set("contact_phone",contactPhone);
        return this;
    }
    public String getContactFax(){
        return getStr("contact_fax");
    }
    public Quotes setContactFax(String contactFax){
        set("contact_fax",contactFax);
        return this;
    }
    public String getContactEmail(){
        return getStr("contact_email");
    }
    public Quotes setContactEmail(String contactEmail){
        set("contact_email",contactEmail);
        return this;
    }
    public String getBillTo(){
        return getStr("bill_to");
    }
    public Quotes setBllTo(String billTo){
        set("bill_to",billTo);
        return this;
    }
    public String getShipTo(){
        return getStr("ship_to");
    }
    public Quotes setShipTo(String shipTo ){
        set("ship_to",shipTo);
        return this;
    }
    public String getAuthor(){
        return getStr("author");
    }
    public Quotes setAuthor(String author){
        set("author",author);
        return this;
    }
    public String getMessage(){
        return getStr("message");
    }
    public Quotes setMessage(String message){
        set("message",message);
        return this;
    }
    public String getPrimaryRecipient(){
        return getStr("primaryRecipient");
    }
    public Quotes setPrimaryRecipient(String primaryRecipient){
        set("primary_recipient",primaryRecipient);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public Quotes setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public Quotes findById(String dataId){
        return this.dao.findFirst("select * from " + getTableName() + " where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/Repair.java
New file
@@ -0,0 +1,52 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "repair", clazz = Repair.class)
public class Repair extends BaseModel<Repair>{
    public static final Repair dao = new Repair();
    public Long getDataId(){
        return getLong("id");
    }
    public Repair setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getTelephonen(){
        return getStr("telephonen");
    }
    public Repair setTelephonen(String telephonen){
        set("telephonen", telephonen);
        return this;
    }
    public String getContactD(){
        return getStr("contact_d");
    }
    public Repair setContactD(String contactD){
        set("contact_d", contactD);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public Repair setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public Repair findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/Swo.java
New file
@@ -0,0 +1,119 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "swo",clazz = Swo.class)
public class Swo extends BaseModel<Swo> {
    public static final Swo dao= new Swo();
    public Long getDataId(){
        return getLong("id");
    }
    public Swo setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getContactNameHidden(){
        return getStr("contact_name_hidden");
    }
    public Swo setContactNameHidden(String contactNameHidden){
        set("contact_name_hidden",contactNameHidden);
        return this;
    }
    public String getEmail(){
        return getStr("email");
    }
    public Swo setEmail(String email){
        set("email",email);
        return this;
    }
    public String getContact(){
        return getStr("contact");
    }
    public Swo setContact(String contact){
        set("contact",contact);
        return this;
    }
    public String getAuthor(){
        return getStr("author");
    }
    public Swo setAuthor(String author){
        set("author",author);
        return this;
    }
    public String getMessage(){
        return getStr("message");
    }
    public Swo setMessage(String message){
        set("message",message);
        return this;
    }
    public String getPrimaryRecipient(){
        return getStr("primary_recipient");
    }
    public Swo setPrimaryRecipient(String primaryRecipient){
        set("primary_recipient",primaryRecipient);
        return this;
    }
    public String getSend(){
        return getStr("send");
    }
    public Swo setSend(String send){
        set("send",send);
        return this;
    }
    public String getCc(){
        return getStr("cc");
    }
    public Swo setCc(String cc){
        set("cc",cc);
        return this;
    }
    public String getCcName(){
        return get("cc_name");
    }
    public Swo setCcName(String ccName){
        set("cc_name",ccName);
        return this;
    }
    public String getBcc(){
        return getStr("bcc");
    }
    public Swo setBcc(String bcc){
        set("bcc",bcc);
        return this;
    }
    public String getBccName(){
        return getStr("bcc_name");
    }
    public Swo setBccName(String bccName){
        set("bcc_name",bccName);
        return this;
    }
    public String getRecipient(){
        return getStr("recipient");
    }
    public Swo setRecipient(String recipient){
        set("recipient",recipient);
        return this;
    }
    public String getToName(){
        return getStr("to_name");
    }
    public Swo setToName(String toName){
        set("to_name",toName);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public Swo setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public Swo findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/SysLog.java
New file
@@ -0,0 +1,29 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
/**
 * @author 廖振钦
 * @date 2022-01-14
 */
@Table(tableName="sys_log", clazz = SysLog.class)
public class SysLog extends BaseModel<SysLog> {
    public final static SysLog dao = new SysLog();
    public SysLog ip(String ip){
        set("ip", ip);
        return this;
    }
    public SysLog uri(String uri){
        set("uri", uri);
        return this;
    }
    public SysLog retContent(String retContent){
        set("ret_content", retContent);
        return this;
    }
}
src/main/java/com/deloitte/system/model/TSRepair.java
New file
@@ -0,0 +1,49 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "ts_repair",clazz = TSRepair.class)
public class TSRepair extends BaseModel<TSRepair> {
    public static final TSRepair dao =new TSRepair();
    public Long getDataId(){
        return getLong("id");
    }
    public TSRepair setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getBusinessAContact(){
        return getStr("business_a_contact");
    }
    public TSRepair setBusinessAContact(String businessAContact){
        set("business_A_contact", businessAContact);
        return this;
    }
    public String getBusinessAPhone(){
        return getStr("business_a_phone");
    }
    public TSRepair setBusinessAPhone(String businessAPhone){
        set("business_a_phone",businessAPhone);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public TSRepair setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public TSRepair findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/model/UserFaultInfo.java
New file
@@ -0,0 +1,61 @@
package com.deloitte.system.model;
import com.common.annotation.Table;
import com.common.core.domain.BaseModel;
@Table(tableName = "user_fault_info",clazz = UserFaultInfo.class)
public class UserFaultInfo extends BaseModel<UserFaultInfo> {
    public static  final UserFaultInfo dao =new UserFaultInfo();
    public Long getDataId(){
        return getLong("id");
    }
    public UserFaultInfo setDataId(Long dataId){
        set("id", dataId);
        return this;
    }
    public String getUfContact(){
        return getStr("uf_contact");
    }
    public UserFaultInfo setUfContact(String ufContact){
        set("uf_contact",ufContact);
        return this;
    }
    public String getInboundEmailAddress(){
        return getStr("inbound_email_address");
    }
    public UserFaultInfo setInboundEmailAddress(String inboundEmailAddress){
        set("inbound_email_address",inboundEmailAddress);
        return this;
    }
    public String getUfPhone(){
        return getStr("uf_phone");
    }
    public UserFaultInfo setUfPhone(String ufPhone){
        set("uf_phone",ufPhone);
        return this;
    }
    public String getSfRecordId(){
        return getStr("sf_record_id");
    }
    public UserFaultInfo setSfRecordId(String sfRecordId){
        set("sf_record_id", sfRecordId);
        return this;
    }
    public UserFaultInfo findById(String dataId){
        return this.dao.findFirst("select * from `" + getTableName() + "` where id=? ", dataId);
    }
}
src/main/java/com/deloitte/system/request/CampaignUserAllDto.java
New file
@@ -0,0 +1,40 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import com.opencsv.bean.CsvBindByName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CampaignUserAllDto {
    private String dataId;
    @CsvBindByName(column = "deptName")
    private String deptName;
    @CsvBindByName(column = "productDemand")
    private String productDemand;
    @CsvBindByName(column = "province")
    private String province;
    @CsvBindByName(column = "remarks")
    private String remarks;
    @CsvBindByName(column = "time")
    private String time;
    private String exhibition;
    @CsvBindByName(column = "name")
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String name;
    @CsvBindByName(column = "email")
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String email;
    @CsvBindByName(column = "phone")
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String phone;
    private String nameEncrypt;
    private String emailEncrypt;
    private String phoneEncrypt;
}
src/main/java/com/deloitte/system/request/CampaignUserDto.java
New file
@@ -0,0 +1,27 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CampaignUserDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String name;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String email;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String phone;
    private String nameEncrypt;
    private String emailEncrypt;
    private String phoneEncrypt;
}
src/main/java/com/deloitte/system/request/ContactDto.java
New file
@@ -0,0 +1,96 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ContactDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String firstName;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String lastName;
    @DesensitiveInfo(DesensitiveType.CNAPS_CODE)
    private String postcode;
    @DesensitiveInfo(DesensitiveType.CNAPS_CODE)
    private String postcodeD;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String titleD;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String title;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String contactEnglishName;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String englishAddress;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String address1;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String address2;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String address3;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String address1D;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String address2D;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String address3D;
    @DesensitiveInfo(DesensitiveType.BASIC)
    private String fax;
    @DesensitiveInfo(DesensitiveType.BASIC)
    private String faxD;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String emailD;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String email;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String mobilePhoneD;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String otherPhoneD;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String phoneD;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String homePhone;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String mobilePhone;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String otherPhone;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String phone;
    private String firstNameEncrypt;
    private String lastNameEncrypt;
    private String postcodeEncrypt;
    private String postcodeDEncrypt;
    private String titleDEncrypt;
    private String titleEncrypt;
    private String contactEnglishNameEncrypt;
    private String englishAddressEncrypt;
    private String address1Encrypt;
    private String address2Encrypt;
    private String address3Encrypt;
    private String address1DEncrypt;
    private String address2DEncrypt;
    private String address3DEncrypt;
    private String faxEncrypt;
    private String faxDEncrypt;
    private String emailDEncrypt;
    private String emailEncrypt;
    private String mobilePhoneDEncrypt;
    private String otherPhoneDEncrypt;
    private String phoneDEncrypt;
    private String homePhoneEncrypt;
    private String mobilePhoneEncrypt;
    private String otherPhoneEncrypt;
    private String phoneEncrypt;
}
src/main/java/com/deloitte/system/request/ContactSearchDto.java
New file
@@ -0,0 +1,16 @@
package com.deloitte.system.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ContactSearchDto {
    private List<String> dataIds;
    private String contactName;
}
src/main/java/com/deloitte/system/request/FileRequest.java
New file
@@ -0,0 +1,22 @@
package com.deloitte.system.request;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
 * @Author holfeng
 * @Date 11:38 11/02/2022
 * @Version 1.0
 **/
@Data
public class FileRequest {
    @NotNull(message = "文件不能为空")
    private String file;
    @NotNull(message = "文件大小不能为空")
    private Integer size;
    @NotNull(message = "文件名不能为空")
    private String fileName;
}
src/main/java/com/deloitte/system/request/LoanerApplicationDto.java
New file
@@ -0,0 +1,50 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoanerApplicationDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String applyPerson;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String applyPersonPhone;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String applicantMailbox;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String loanerReceiveStaff;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String directShippmentAddress;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String loanerReceiveStaffPhone;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String returnTrakeStaff;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String returnNumber;
    @DesensitiveInfo(DesensitiveType.ZIP_CODE)
    private String postCode;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String loanerSer;
    private String applyPersonEncrypt;
    private String applyPersonPhoneEncrypt;
    private String applicantMailboxEncrypt;
    private String loanerReceiveStaffEncrypt;
    private String directShippmentAddressEncrypt;
    private String loanerReceiveStaffPhoneEncrypt;
    private String returnTrakeStaffEncrypt;
    private String returnNumberEncrypt;
    private String postCodeEncrypt;
    private String loanerSerEncrypt;
}
src/main/java/com/deloitte/system/request/LoanerUserDto.java
New file
@@ -0,0 +1,24 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoanerUserDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String contact;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String contactNumber;
    private String contactEncrypt;
    private String contactNumberEncrypt;
}
src/main/java/com/deloitte/system/request/MailMergeDto.java
New file
@@ -0,0 +1,102 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.annotation.NoEncryption;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MailMergeDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @NoEncryption
    private String name;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String cc;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String bcc;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String author;
    @DesensitiveInfo(DesensitiveType.BASIC)
    private String allMember;
    @NoEncryption
    private String allMemberType;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String toName;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String allMemberName;
    @NoEncryption
    private String mailType;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String ccName;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String bccName;
    @NoEncryption
    private String type;
    @NoEncryption
    private String subject;
    @NoEncryption
    private String subjectCopy;
    @NoEncryption
    private String record;
    @NoEncryption
    private String recordType;
    @DesensitiveInfo(DesensitiveType.BASIC)
    private String recipient;
    @DesensitiveInfo(DesensitiveType.BASIC)
    private String premaryRecipient;
    @NoEncryption
    private String files;
    @NoEncryption
    private String emalSent;
    @NoEncryption
    private String emailSent;
    @NoEncryption
    private String currencyIsoCode;
    @NoEncryption
    private String internalOnly;
    @NoEncryption
    private String ownerId;
    @NoEncryption
    private String lastModifiedById;
    @NoEncryption
    private String createdById;
    @NoEncryption
    private String swo;
    @NoEncryption
    private String quotes;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String send;
    @NoEncryption
    private String caseF;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String from;
    @NoEncryption
    private Date date;
    @DesensitiveInfo(DesensitiveType.BASIC)
    private String message;
    private String ccEncrypt;
    private String bccEncrypt;
    private String authorEncrypt;
    private String allMemberEncrypt;
    private String toNameEncrypt;
    private String allMemberNameEncrypt;
    private String ccNameEncrypt;
    private String bccNameEncrypt;
    private String recipientEncrypt;
    private String premaryRecipientEncrypt;
    private String sendEncrypt;
    private String fromEncrypt;
    private String messageEncrypt;
}
src/main/java/com/deloitte/system/request/MessageVo.java
New file
@@ -0,0 +1,28 @@
package com.deloitte.system.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MessageVo {
    //分号分割
    private String bcc;
    private String cc;
    private String recipient;//to收件人
    private String from;
    private String message;
    private String recordType;//SWO,Case,Quotes
    private String recordId;//SWO,Case,Quotes
    private String subjectCopy;
    private String bccName;
    private String ccName;
    private String toName;
    private String fromName;
    private Map<String,String> fileMap;
}
src/main/java/com/deloitte/system/request/OperatorQueryDto.java
New file
@@ -0,0 +1,18 @@
package com.deloitte.system.request;
import lombok.Data;
/**
 * 带操作符的查询条件
 *
 * @author changzheng
 * Created on 16/09/2022
 * {"name":"sfRecordId","operator":"in","value":["0031000000SFB0C","0031000001VIfpJ"]}
 */
@Data
public class OperatorQueryDto {
    String name;
    String operator;
    Object value;
}
src/main/java/com/deloitte/system/request/OpportunityDto.java
New file
@@ -0,0 +1,44 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OpportunityDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String dealerServiceD;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String dealerService;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String dealerSalesStaffNameD;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String dealerSalesStaffName;
    @DesensitiveInfo(DesensitiveType.Date)
    private String expectedDeliveryDate;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String salesAccountCode;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String specialDeliveryAddress;
    private String dealerServiceDEncrypt;
    private String dealerServiceEncrypt;
    private String dealerSalesStaffNameDEncrypt;
    private String dealerSalesStaffNameEncrypt;
    private String expectedDeliveryDateEncrypt;
    private String salesAccountCodeEncrypt;
    private String specialDeliveryAddressEncrypt;
}
src/main/java/com/deloitte/system/request/OrderDto.java
New file
@@ -0,0 +1,143 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String specialDeliveryPhone;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String specialDeliveryPhoneD;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String shippingRecieverEmailAdr;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String shipToContactId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String customerAuthorizedById;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String billToContactId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String specialDeliveryContact2;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String specialDeliveryContactText;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String shippingAddressText;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String specialDeliveryContact2D;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String endUser;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String endUserD;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String specialDeliveryContact;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String specialDeliveryContactD;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfNNotifyParty;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String pdfNFax;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfNContact;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String pdfSTel;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String pdfSPhone;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfSName;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String pdfSFax;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String pdfFTel;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String pdfFFax;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfFContactPerson;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String pdfSAdds;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String pdfSAddress;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfSeller;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfCTheconsigne;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String pdfCTel;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String pdfCFax;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfCConsignee;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfCContact;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String pdfCAddr;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String specialDeliveryAddress;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String specialDeliveryAddressD;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String dealerSalesStaffNameA;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String pdfSignTitle;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String pdfSignName;
    @DesensitiveInfo(DesensitiveType.TITLE_TYPE)
    private String pdfSignaturePlaces;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String pdfByTel;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String pdfByAdd;
    private String specialDeliveryPhoneEncrypt;
    private String specialDeliveryPhoneDEncrypt;
    private String shippingRecieverEmailAdrEncrypt;
    private String shipToContactIdEncrypt;
    private String customerAuthorizedByIdEncrypt;
    private String billToContactIdEncrypt;
    private String specialDeliveryContact2Encrypt;
    private String specialDeliveryContactTextEncrypt;
    private String shippingAddressTextEncrypt;
    private String specialDeliveryContact2DEncrypt;
    private String endUserEncrypt;
    private String endUserDEncrypt;
    private String specialDeliveryContactEncrypt;
    private String specialDeliveryContactDEncrypt;
    private String pdfNNotifyPartyEncrypt;
    private String pdfNFaxEncrypt;
    private String pdfNContactEncrypt;
    private String pdfSTelEncrypt;
    private String pdfSPhoneEncrypt;
    private String pdfSNameEncrypt;
    private String pdfSFaxEncrypt;
    private String pdfFTelEncrypt;
    private String pdfFFaxEncrypt;
    private String pdfFContactPersonEncrypt;
    private String pdfSAddsEncrypt;
    private String pdfSAddressEncrypt;
    private String pdfSellerEncrypt;
    private String pdfCTheconsigneEncrypt;
    private String pdfCTelEncrypt;
    private String pdfCFaxEncrypt;
    private String pdfCConsigneeEncrypt;
    private String pdfCContactEncrypt;
    private String pdfCAddrEncrypt;
    private String specialDeliveryAddressEncrypt;
    private String specialDeliveryAddressDEncrypt;
    private String dealerSalesStaffNameAEncrypt;
    private String pdfSignTitleEncrypt;
    private String pdfSignNameEncrypt;
    private String pdfSignaturePlacesEncrypt;
    private String pdfByTelEncrypt;
    private String pdfByAddEncrypt;
}
src/main/java/com/deloitte/system/request/QuotesDto.java
New file
@@ -0,0 +1,46 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class QuotesDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String contactName;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String contactPhone;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String contactFax;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String contactEmail;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String billTo;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String shipTo;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String author;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String message;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String primaryRecipient;
    private String contactNameEncrypt;
    private String contactPhoneEncrypt;
    private String contactFaxEncrypt;
    private String contactEmailEncrypt;
    private String billToEncrypt;
    private String shipToEncrypt;
    private String authorEncrypt;
    private String messageEncrypt;
    private String primaryRecipientEncrypt;
}
src/main/java/com/deloitte/system/request/RepairDto.java
New file
@@ -0,0 +1,24 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RepairDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String telephonen;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String contactD;
    private String telephonenEncrypt;
    private String contactDEncrypt;
}
src/main/java/com/deloitte/system/request/SFFileAddressVo.java
New file
@@ -0,0 +1,16 @@
package com.deloitte.system.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SFFileAddressVo {
    private String AWS_File_Key__c;
    private String FileName__c;
    private String DownloadLink__c;
    private String ParentRecordId__c;
    private String ViewLink__c;
}
src/main/java/com/deloitte/system/request/SFMessageVo.java
New file
@@ -0,0 +1,47 @@
package com.deloitte.system.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SFMessageVo {
    private String Name;
    private String ALL_MEMBER_NAME__c;
    private String ALL_MEMBER_TYPE__c;
    private String ALL_MEMBER__c;
    private String BCC__c;
    private String CC__c;
    private String FROM__c;
    private String MESSAGE__c;
    private String RECIPIENT__c;
    private String RECORD__c;
    private String RECORD_TYPE__c;
    private String SUBJECTCOPY__c;
    private String SUBJECT__c;
    private String TYPE__c;
    private String bccName__c;
    private String ccName__c;
    private String toName__c;
    private String AWS_Data_Id__c;
    private String DATE__c;
    private String ALL_MEMBER_NAME_Encrypted__c;
    private String ALL_MEMBER_Encrypted__c;
    private String BCC_Encrypted__c ;
    private String CC_Encrypted__c;
    private String FROM_Encrypted__c;
    private String RECIPIENT_Encrypted__c;
    private String bccName_Encrypted__c;
    private String ccName_Encrypted__c;
    private String toName_Encrypted__c;
    private String SWO__c;
    private String Quotes__c;
    private String CaseF__c;
    private String EMAIL_SENT__c;
}
src/main/java/com/deloitte/system/request/SFTokenDto.java
New file
@@ -0,0 +1,26 @@
package com.deloitte.system.request;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SFTokenDto {
    /** 接口调用凭证 access_token */
    @JsonProperty("access_token")
    private String accessToken;
    /** 服务器地址*/
    @JsonProperty("instance_url")
    private String instanceUrl;
    /** 当前请求用户的身份*/
    private String id;
    /** 服务器地址*/
    @JsonProperty("token_type")
    private String tokenType;
    /** 生成签名的时间*/
    @JsonProperty("issued_at")
    private String issuedAt;
    /** 用client secret密钥*/
    private String signature;
}
src/main/java/com/deloitte/system/request/SearchDto.java
New file
@@ -0,0 +1,16 @@
package com.deloitte.system.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SearchDto {
    private List<String> dataIds;
    private String name;
}
src/main/java/com/deloitte/system/request/SfCompositeRequest.java
New file
@@ -0,0 +1,57 @@
package com.deloitte.system.request;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
 * 邮件内容同步至SF request
 *
 * @author Ali
 * Created on 22/03/2022
 */
@Data
public class SfCompositeRequest {
    private boolean allOrNone = true;
    private List<JSONObject> compositeRequest = new ArrayList<>();
    private List<SFFileAddressVo> fileAddressList = new ArrayList<>();
    public void addMailMerge(SFMessageVo messageVo){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("method","POST");
        jsonObject.put("referenceId","NewMailMerge");
        jsonObject.put("url","/services/data/v53.0/sobjects/Mail_Merge__c/");
        jsonObject.put("body",messageVo);
        compositeRequest.add(jsonObject);
    }
    public void addFileAddress(List<SFFileAddressVo> fileAddressVos){
        for (SFFileAddressVo fileAddressVo:fileAddressVos) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("method","POST");
            String referenceId = "NewFileAddress";
            if(fileAddressList!=null && fileAddressList.size()>0){
                referenceId+=fileAddressList.size();
            }
            jsonObject.put("referenceId",referenceId);
            jsonObject.put("url","/services/data/v53.0/sobjects/FileAddress__c");
            jsonObject.put("body",fileAddressVo);
            fileAddressList.add(fileAddressVo);
            compositeRequest.add(jsonObject);
        }
    }
    // 获取请求json
    public String toJsonString() {
        return "{" +
                "\"allOrNone\":" + allOrNone +
                ", \"compositeRequest\":" + compositeRequest +
                '}';
    }
}
src/main/java/com/deloitte/system/request/SwoDto.java
New file
@@ -0,0 +1,58 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SwoDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String contactNameHidden;
    @DesensitiveInfo(DesensitiveType.EMAIL)
    private String email;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String contact;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String author;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String message;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String primaryRecipient;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String send;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String cc;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String ccName;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String bcc;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String bccName;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String recipient;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String toName;
    private String contactNameHiddenEncrypt;
    private String emailEncrypt;
    private String contactEncrypt;
    private String authorEncrypt;
    private String messageEncrypt;
    private String primaryRecipientEncrypt;
    private String sendEncrypt;
    private String ccEncrypt;
    private String ccNameEncrypt;
    private String bccEncrypt;
    private String bccNameEncrypt;
    private String recipientEncrypt;
    private String toNameEncrypt;
}
src/main/java/com/deloitte/system/request/TSRepairDto.java
New file
@@ -0,0 +1,24 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TSRepairDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String businessAContact;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String businessAPhone;
    private String businessAPhoneEncrypt;
    private String businessAContactEncrypt;
}
src/main/java/com/deloitte/system/request/TxConfirmDto.java
New file
@@ -0,0 +1,29 @@
package com.deloitte.system.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TxConfirmDto {
    private String txId;
    private String sfRecordId;
    private Integer isSuccess;
    private List<ConfimrListDto> idList;
    public TxConfirmDto(String txId, String sfRecordId, Integer isSuccess) {
        this.txId = txId;
        this.sfRecordId = sfRecordId;
        this.isSuccess = isSuccess;
    }
    @Data
    public static class ConfimrListDto{
        private String sfRecordId;
        private String awsId;
    }
}
src/main/java/com/deloitte/system/request/UserFaultInfoDto.java
New file
@@ -0,0 +1,27 @@
package com.deloitte.system.request;
import com.common.annotation.DesensitiveInfo;
import com.common.core.domain.DesensitiveType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserFaultInfoDto {
    private String dataId;
    private Integer isDelete;
    private String sfRecordId;
    @DesensitiveInfo(DesensitiveType.CHINESE_NAME)
    private String ufContact;
    @DesensitiveInfo(DesensitiveType.ADDRESS)
    private String inboundEmailAddress;
    @DesensitiveInfo(DesensitiveType.MOBILE_PHONE)
    private String ufPhone;
    private String ufContactEncrypt;
    private String inboundEmailAddressEncrypt;
    private String ufPhoneEncrypt;
}
src/main/java/com/deloitte/system/service/CampaignUserService.java
New file
@@ -0,0 +1,206 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DateUtils;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.core.utils.StringUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.CampaignUser;
import com.deloitte.system.model.Contact;
import com.deloitte.system.request.CampaignUserAllDto;
import com.deloitte.system.request.CampaignUserDto;
import com.deloitte.system.request.ContactDto;
import com.deloitte.system.request.OperatorQueryDto;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Slf4j
@Service
public class CampaignUserService {
    private String tableName="campaign_user";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public CampaignUserDto queryForOne(String dataId) {
        CampaignUser campaignUser =CampaignUser.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(campaignUser)){
            CampaignUserDto dto = BeanHelper.copyAs(campaignUser, CampaignUserDto.class);
            return dto;
        }
        return null;
    }
    public List<CampaignUserDto> insertList(List<CampaignUserDto> campaignUserDtoList, String txId) {
        List<CampaignUserDto> respList = new ArrayList<>();
        CacheList<CampaignUserDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<CampaignUserDto> dtoItemList = new ArrayList<>();
        campaignUserDtoList.forEach(e -> {
            String dataId = idWorker.nextId();
            CampaignUserDto cacheItem = BeanHelper.copyAs(e, CampaignUserDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            CampaignUserDto target = BeanHelper.copyAs(e, CampaignUserDto.class);
            target.setDataId(dataId);
            CampaignUserDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setNameEncrypt(dto.getName());
            target.setPhoneEncrypt(dto.getPhone());
            target.setEmailEncrypt(dto.getEmail());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<CampaignUserDto> updateList(List<CampaignUserDto> campaignUserDtoList, String txId) {
        List<CampaignUserDto> respList = new ArrayList<>();
        CacheList<CampaignUserDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<CampaignUserDto> dtoItemList = new ArrayList<>();
        campaignUserDtoList.forEach(e -> {
            CampaignUser campaignUser = CampaignUser.dao.findById(e.getDataId());
            CampaignUserDto cacheItem = BeanHelper.copyAs(e, CampaignUserDto.class);
            dtoItemList.add(cacheItem);
            CampaignUserDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,campaignUser),CampaignUserDto.class);
            CampaignUserDto target = BeanHelper.copyAs(dto, CampaignUserDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setNameEncrypt(dto.getName());
            target.setPhoneEncrypt(dto.getPhone());
            target.setEmailEncrypt(dto.getEmail());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        CampaignUser campaignUser = CampaignUser.dao.findById(dataId);
        campaignUser.setIsDelete(1);
        return campaignUser.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        CampaignUser campaignUser = CampaignUser.dao.findById(dataId);
        campaignUser.setIsDelete(0);
        return campaignUser.saveOrUpdate();
    }
    public List<CampaignUserDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<CampaignUser> quotesList = CampaignUser.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, CampaignUserDto.class)).collect(Collectors.toList());
    }
    public List<CampaignUserAllDto> batchUpload(MultipartFile[] files,String txId) {
        List<CampaignUserAllDto> listAll= null;
        try {
            InputStreamReader in = null;
            in = new InputStreamReader(files[0].getInputStream(), FileService.getFilecharset(files[0].getInputStream()));
            HeaderColumnNameMappingStrategy<CampaignUserAllDto> strategy = new HeaderColumnNameMappingStrategy<>();
            strategy.setType(CampaignUserAllDto.class);
            CsvToBean<CampaignUserAllDto> csvToBean = new CsvToBeanBuilder<CampaignUserAllDto>(in)
                    .withSeparator(',')
                    .withQuoteChar('\'')
                    .withMappingStrategy(strategy).build();
            listAll= csvToBean.parse();
            List<CampaignUserDto> userList=BeanHelper.copyAs(listAll,CampaignUserDto.class);
            List<CampaignUserDto> userList2 = this.insertList(userList,txId);
            for (int i = 0; i < listAll.size(); i++) {
                BeanHelper.copy(userList2.get(i),listAll.get(i));
            }
        } catch (Exception e) {
            log.error("CampaignUser batchUpload failed:",e);
            throw new BizException(ResultCodeEnum.AWS_RT_ERROR);
        }
        //日期转换
        /*if(null!=listAll && !listAll.isEmpty()){
            listAll.forEach(e->{
                Date date = DateUtils.convert(e.getDateTime(),DateUtils.DATE_TIME_MINUTE_FORMAT2);
                e.setDateTime(DateUtils.format(date,DateUtils.DATE_TIME_MINUTE_FORMAT1));
            });
        }*/
        return listAll;
    }
    public List<CampaignUserDto> searchListByOperator(List<OperatorQueryDto> queryDtos) {
        StringBuilder queryParam = new StringBuilder();
        for (OperatorQueryDto queryDto: queryDtos) {
            String queryName = queryDto.getName();
            //name驼峰转下划线
            String name = BeanHelper.propertyToColumn(queryName);
            String operator = queryDto.getOperator();
            Object value = queryDto.getValue();
            if(StringUtils.isNotEmpty(operator)){
                if("=".equals(operator)||"!=".equals(operator)){
                    queryParam.append(" and c."+name).append(" "+operator+" ").append("'"+value+"'");
                }else if("like".equals(operator)||"not like".equals(operator)){
                    queryParam.append(" and c."+name).append(" "+operator+" ").append("\'%").append(value).append("%\'");
                }else if("in".equals(operator)||"not in".equals(operator)){
                    List<String> value1 = (ArrayList<String>)value;
                    queryParam.append(" and c."+name).append(" "+operator+" ").append(value1.stream().collect(Collectors.joining("','", "('", "')")));
                }else {
                    throw new BizException("无效的sql请求!");
                }
            }
        }
        queryParam.append(" and c.sf_record_id is not null");
        String sql = "select c.* from campaign_user c WHERE c.is_delete = '0' "+queryParam.toString();
        List<CampaignUser> campaignUserList = CampaignUser.dao.findListBySql(sql);
        return campaignUserList.stream().map(e->BeanHelper.copyAs(e,CampaignUserDto.class)).collect(Collectors.toList());
    }
}
src/main/java/com/deloitte/system/service/ConfirmTxService.java
New file
@@ -0,0 +1,155 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.common.annotation.Table;
import com.common.aws.util.S3Util;
import com.common.core.constant.Constants;
import com.common.core.constant.GlobalConst;
import com.common.core.domain.BaseModel;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.enums.YesNoEnum;
import com.common.core.exception.BizException;
import com.common.core.utils.DistributedLock;
import com.common.core.utils.KitClassUtils;
import com.common.core.utils.ModelUtils;
import com.common.core.utils.StringUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.request.TxConfirmDto;
import com.jfinal.plugin.activerecord.Db;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ConfirmTxService {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private DistributedLock distributedLock;
    @Value("${aws.s3.bucketName}")
    private String bucketName;
    public void confirm(TxConfirmDto dto) {
        String txid=dto.getTxId();
        String sfRecordId = dto.getSfRecordId();
        if(null== redisUtil.get(GlobalConst.PIPL_UNCONFIRM_INSERT + txid) && null == redisUtil.get(GlobalConst.PIPL_UNCONFIRM_UPDATE + txid)){
            throw new BizException("不存在的事务ID(txid)");
        }
        //遍历insert
        Object object = redisUtil.get(GlobalConst.PIPL_UNCONFIRM_INSERT + txid);
        if(object!=null && StringUtils.isNotEmpty(object.toString()) && distributedLock.getLock(GlobalConst.PIPL_UNCONFIRM_LOCK +txid,3600 * 1000)) {
            try {
                log.info(object.toString());
                if(YesNoEnum.YES.getCode().equals(dto.getIsSuccess())) {
                    List<BaseModel> models = generateModelInfo(object, sfRecordId,dto.getIdList(), true);
                    Db.tx(() -> {
                        for (BaseModel model : models) {
                            if(!model.save()) return false;
                        }
                        return true;
                    });
                }
                redisUtil.deleteKey(GlobalConst.PIPL_UNCONFIRM_INSERT + txid);
            }catch (Exception e){
                log.error(e.getMessage(), e);
                throw new BizException(ResultCodeEnum.RT_ERROR);
            }finally {
                distributedLock.releaseLock(GlobalConst.PIPL_UNCONFIRM_LOCK +txid);
            }
        }
        //遍历update
        object = redisUtil.get(GlobalConst.PIPL_UNCONFIRM_UPDATE + txid);
        if(object!=null && StringUtils.isNotEmpty(object.toString()) && distributedLock.getLock(GlobalConst.PIPL_UNCONFIRM_LOCK +txid,3600 * 1000)) {
            try {
                log.info(object.toString());
                if(YesNoEnum.YES.getCode().equals(dto.getIsSuccess())) {
                    List<BaseModel> models = generateModelInfo(object,sfRecordId,dto.getIdList(),false);
                    Db.tx(() -> {
                        for (BaseModel model : models) {
                            if(!model.update()) return false;
                        }
                        return true;
                    });
                }
                redisUtil.deleteKey(GlobalConst.PIPL_UNCONFIRM_UPDATE + txid);
            }catch (Exception e){
                log.error(e.getMessage(), e);
                throw new BizException(ResultCodeEnum.RT_ERROR);
            }finally {
                distributedLock.releaseLock(GlobalConst.PIPL_UNCONFIRM_LOCK +txid);
            }
        }
    }
    public void confirmFile(TxConfirmDto dto) {
        String txid = dto.getTxId();
        if(null== redisUtil.get(GlobalConst.PIPL_UNCONFIRM_FILE + txid)){
            throw new BizException("不存在的事务ID(txid)");
        }
        Object object = redisUtil.get(GlobalConst.PIPL_UNCONFIRM_FILE + txid);
        if(object!=null && StringUtils.isNotEmpty(object.toString()) && distributedLock.getLock(GlobalConst.PIPL_UNCONFIRM_LOCK +txid,3600 * 1000)) {
           try {
               if(YesNoEnum.NO.getCode().equals(dto.getIsSuccess())) {
                   List<String> keys = JSONArray.parseArray(object.toString(),String.class);
                   S3Util.deleteByKeys(keys,bucketName);
               }
               redisUtil.deleteKey(GlobalConst.PIPL_UNCONFIRM_FILE + txid);
           }catch (Exception e){
               log.error(e.getMessage(), e);
               throw new BizException(ResultCodeEnum.RT_ERROR);
           }finally {
               distributedLock.releaseLock(GlobalConst.PIPL_UNCONFIRM_LOCK +txid);
           }
        }
    }
    private List<BaseModel> generateModelInfo(Object object, String sfRecordId, List<TxConfirmDto.ConfimrListDto> idList,boolean isCreate) throws InstantiationException, IllegalAccessException {
        JSONObject jsonobject = JSONObject.parseObject(object.toString());
        JSONArray dataArray = jsonobject.getJSONArray("data");
        String table_name = jsonobject.getString("tableName");
        List<BaseModel> models = new ArrayList<>();
        Map<String,String> idMap= null;
        if(CollectionUtil.isNotEmpty(idList)){
            idMap=idList.stream().collect(Collectors.toMap(TxConfirmDto.ConfimrListDto::getAwsId, TxConfirmDto.ConfimrListDto::getSfRecordId));
        }
        for (Class<?> clazz : KitClassUtils.tableclass) {
            Table table = clazz.getAnnotation(Table.class);
            String tablename = table.tableName();
            if (tablename.equals(table_name)) {
                Class<? extends BaseModel<?>> classmodel = table.clazz();
                for (int i = 0; i < dataArray.size(); i++) {
                    JSONObject jsonObject = dataArray.getJSONObject(i);
                    BaseModel baseModel = ModelUtils.JsonToModel(jsonObject, classmodel.newInstance());
                    if(idMap!=null && CollectionUtil.isNotEmpty(idMap)){
                        baseModel.set("sf_record_id",idMap.get(baseModel.getStr("id")));
                    }else {
                        if (StrUtil.isNotBlank(sfRecordId)) {
                            baseModel.set("sf_record_id", sfRecordId);
                        }
                    }
                    if(isCreate){
                        ModelUtils.generateCommonInfo(baseModel, Constants.ACTION_CREATE);
                    }else {
                        ModelUtils.generateCommonInfo(baseModel, Constants.ACTION_UPDATE);
                    }
                    models.add(baseModel);
                }
            }
        }
        return models;
    }
}
src/main/java/com/deloitte/system/service/ContactService.java
New file
@@ -0,0 +1,268 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.*;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.Contact;
import com.deloitte.system.request.ContactDto;
import com.deloitte.system.request.ContactSearchDto;
import com.jfinal.plugin.activerecord.Db;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class ContactService {
    private String tableName = "contact";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public ContactDto queryForOne(String dataId) {
        Contact contact = Contact.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(contact)) {
            return BeanHelper.copyAs(contact,ContactDto.class);
        }
        return null;
    }
    public List<ContactDto> insertList(List<ContactDto> contactList, String txId) {
        List<ContactDto> respList = new ArrayList<>();
        CacheList<ContactDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<ContactDto> dtoItemList = new ArrayList<>();
        contactList.forEach(e -> {
            String dataId = idWorker.nextId();
            ContactDto cacheItem = BeanHelper.copyAs(e, ContactDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            ContactDto target = BeanHelper.copyAs(e, ContactDto.class);
            target.setDataId(dataId);
            ContactDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setFirstNameEncrypt(dto.getFirstName());
            target.setLastNameEncrypt(dto.getLastName());
            target.setPostcodeEncrypt(dto.getPostcode());
            target.setPostcodeDEncrypt(dto.getPostcodeD());
            target.setTitleDEncrypt(dto.getTitleD());
            target.setTitleEncrypt(dto.getTitle());
            target.setContactEnglishNameEncrypt(dto.getContactEnglishName());
            target.setEnglishAddressEncrypt(dto.getEnglishAddress());
            target.setAddress1Encrypt(dto.getAddress1());
            target.setAddress2Encrypt(dto.getAddress2());
            target.setAddress3Encrypt(dto.getAddress3());
            target.setAddress1DEncrypt(dto.getAddress1D());
            target.setAddress2DEncrypt(dto.getAddress2D());
            target.setAddress3DEncrypt(dto.getAddress3D());
            target.setFaxEncrypt(dto.getFax());
            target.setFaxDEncrypt(dto.getFaxD());
            target.setEmailDEncrypt(dto.getEmailD());
            target.setEmailEncrypt(dto.getEmail());
            target.setMobilePhoneDEncrypt(dto.getMobilePhoneD());
            target.setOtherPhoneDEncrypt(dto.getOtherPhoneD());
            target.setOtherPhoneDEncrypt(dto.getOtherPhoneD());
            target.setPhoneDEncrypt(dto.getPhoneD());
            target.setHomePhoneEncrypt(dto.getHomePhone());
            target.setMobilePhoneEncrypt(dto.getMobilePhone());
            target.setOtherPhoneEncrypt(dto.getOtherPhone());
            target.setPhoneEncrypt(dto.getPhone());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    /** 不存缓存,直接入库 **/
    public List<ContactDto> batchInsert(List<ContactDto> contactList) {
        List<ContactDto> respList = new ArrayList<>();
        CacheList<ContactDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        Db.tx(() -> {
            for (ContactDto e : contactList) {
                String dataId = idWorker.nextId();
                e.setDataId(dataId);
                Contact item= BeanHelper.copyAs(e, Contact.class);
                item.set("id", dataId);
                if (!item.save()) {
                    throw new BizException(ResultCodeEnum.RT_ERROR);
                }
                ContactDto target = BeanHelper.copyAs(e, ContactDto.class);
                target.setDataId(dataId);
                ContactDto dto = cryptoService.encryptModelColumns(e);
                dto.setDataId(dataId);
                target.setFirstNameEncrypt(dto.getFirstName());
                target.setLastNameEncrypt(dto.getLastName());
                target.setPostcodeEncrypt(dto.getPostcode());
                target.setPostcodeDEncrypt(dto.getPostcodeD());
                target.setTitleDEncrypt(dto.getTitleD());
                target.setTitleEncrypt(dto.getTitle());
                target.setContactEnglishNameEncrypt(dto.getContactEnglishName());
                target.setEnglishAddressEncrypt(dto.getEnglishAddress());
                target.setAddress1Encrypt(dto.getAddress1());
                target.setAddress2Encrypt(dto.getAddress2());
                target.setAddress3Encrypt(dto.getAddress3());
                target.setAddress1DEncrypt(dto.getAddress1D());
                target.setAddress2DEncrypt(dto.getAddress2D());
                target.setAddress3DEncrypt(dto.getAddress3D());
                target.setFaxEncrypt(dto.getFax());
                target.setFaxDEncrypt(dto.getFaxD());
                target.setEmailDEncrypt(dto.getEmailD());
                target.setEmailEncrypt(dto.getEmail());
                target.setMobilePhoneDEncrypt(dto.getMobilePhoneD());
                target.setOtherPhoneDEncrypt(dto.getOtherPhoneD());
                target.setOtherPhoneDEncrypt(dto.getOtherPhoneD());
                target.setPhoneDEncrypt(dto.getPhoneD());
                target.setHomePhoneEncrypt(dto.getHomePhone());
                target.setMobilePhoneEncrypt(dto.getMobilePhone());
                target.setOtherPhoneEncrypt(dto.getOtherPhone());
                target.setPhoneEncrypt(dto.getPhone());
                DesensitiveUtils.format(target);
                respList.add(target);
            }
            return true;
        });
        return respList;
    }
    public List<ContactDto> updateList(List<ContactDto> contactList, String txId) {
        List<ContactDto> respList = new ArrayList<>();
        CacheList<ContactDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<ContactDto> dtoItemList = new ArrayList<>();
        contactList.forEach(e -> {
            Contact contact = Contact.dao.findById(e.getDataId());
            ContactDto cacheItem = BeanHelper.copyAs(e, ContactDto.class);
            dtoItemList.add(cacheItem);
            ContactDto dto =BeanHelper.copyAs(BeanHelper.copy(cacheItem,contact),ContactDto.class);
            ContactDto target = BeanHelper.copyAs(dto, ContactDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setFirstNameEncrypt(dto.getFirstName());
            target.setLastNameEncrypt(dto.getLastName());
            target.setPostcodeEncrypt(dto.getPostcode());
            target.setPostcodeDEncrypt(dto.getPostcodeD());
            target.setTitleDEncrypt(dto.getTitleD());
            target.setTitleEncrypt(dto.getTitle());
            target.setContactEnglishNameEncrypt(dto.getContactEnglishName());
            target.setEnglishAddressEncrypt(dto.getEnglishAddress());
            target.setAddress1Encrypt(dto.getAddress1());
            target.setAddress2Encrypt(dto.getAddress2());
            target.setAddress3Encrypt(dto.getAddress3());
            target.setAddress1DEncrypt(dto.getAddress1D());
            target.setAddress2DEncrypt(dto.getAddress2D());
            target.setAddress3DEncrypt(dto.getAddress3D());
            target.setFaxEncrypt(dto.getFax());
            target.setFaxDEncrypt(dto.getFaxD());
            target.setEmailDEncrypt(dto.getEmailD());
            target.setEmailEncrypt(dto.getEmail());
            target.setMobilePhoneDEncrypt(dto.getMobilePhoneD());
            target.setOtherPhoneDEncrypt(dto.getOtherPhoneD());
            target.setPhoneDEncrypt(dto.getPhoneD());
            target.setPhoneEncrypt(dto.getPhone());
            target.setHomePhoneEncrypt(dto.getHomePhone());
            target.setMobilePhoneEncrypt(dto.getMobilePhone());
            target.setOtherPhoneEncrypt(dto.getOtherPhone());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        Contact contact = Contact.dao.findById(dataId);
        contact.setIsDelete(1);
        return contact.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        Contact contact = Contact.dao.findById(dataId);
        contact.setIsDelete(0);
        return contact.saveOrUpdate();
    }
    public List<ContactDto> searchList(ContactSearchDto searchDto) {
        List<String> dataIds = searchDto.getDataIds();
        String contactName = searchDto.getContactName();
        StringBuilder queryParam = new StringBuilder();
        if (CollectionUtil.isEmpty(dataIds) && StringUtils.isEmpty(contactName)) {
            //参数都为空,直接返回Null
            return null;
        } else {
            if(CollectionUtil.isNotEmpty(dataIds)){
                queryParam.append("and id in").append(dataIds.stream().collect(Collectors.joining(", ", "(", ")")));
            }
            if(StringUtils.isNotEmpty(contactName)) {
                queryParam.append("and last_name like ").append("\'%").append(contactName).append("%\'");
            }
            List<Contact> contactList = Contact.dao.findList(queryParam.toString());
            return contactList.stream()
                    .map(contact -> BeanHelper.copyAs(contact,ContactDto.class)).collect(Collectors.toList());
        }
    }
    public List<ContactDto> decryptUpdate(List<ContactDto> contactList) {
        List<ContactDto> respList =new ArrayList<>();
        String[] decryptColumn =new String[]{"first_name","last_name","postcode","postcode_d","title_d","title","phone","address3_d","address2_d","address1_d","address3","address2","address1",
        "english_address","contact_english_name","other_phone","mobile_phone","home_phone","phone_d","other_phone_d","mobile_phone_d","email","email_d","fax_d","fax"};
        Db.tx(() -> {
            for (ContactDto e : contactList) {
                Contact contact =Contact.dao.findById(e.getDataId());
                if (contact == null) {
                    throw new BizException("无效的DataId");
                }
                Contact item=new Contact();
                BeanHelper.copyEncrypt(e,item);
                cryptoService.decryptoColumnModel(item,decryptColumn);
                BeanHelper.copy(item, contact);
                contact.setSfRecordId(e.getSfRecordId());
                if (!contact.saveOrUpdate()) {
                    throw new BizException(ResultCodeEnum.RT_ERROR);
                }
                respList.add(e);
            }
            return true;
        });
        return respList;
    }
}
src/main/java/com/deloitte/system/service/FileService.java
New file
@@ -0,0 +1,377 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.aws.util.S3Util;
import com.common.core.constant.GlobalConst;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.utils.UUID;
import com.common.core.utils.*;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.request.FileRequest;
import com.j256.simplemagic.ContentInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.S3Exception;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_FILE;
@Slf4j
@Service
public class FileService {
    @Autowired
    private RedisUtil redisUtil;
    @Value("${salesforce.referer}")
    private String refererUrl;
    @Value("${aws.s3.bucketName}")
    private String bucketName;
    public String upload(String txId,FileRequest fileRequest){
        log.info("上传文件txId:"+txId);
        String key=uploadFile(fileRequest);
        String keyName = PIPL_UNCONFIRM_FILE + txId;
        redisUtil.set(keyName, new JSONArray(Arrays.asList(key)), GlobalConst.DATA_EXPIRE);
        return key;
    }
    public List<String> batchUpload(String txId, List<FileRequest> fileList) {
        log.info("上传文件txId:"+txId);
        List<String> keys=new ArrayList<>();
        try {
            fileList.forEach(fileRequest -> {
                String key = uploadFile(fileRequest);
                keys.add(key);
            });
            String keyName = PIPL_UNCONFIRM_FILE + txId;
            redisUtil.set(keyName, new JSONArray((List)keys), GlobalConst.DATA_EXPIRE);
        }catch (Exception e){
            this.cleanS3File(keys);
            throw e;
        }
        return keys;
    }
    public String uploadFile(FileRequest fileRequest){
        if (fileRequest == null) {
            throw new BizException(ResultCodeEnum.NO_DATA);
        }
        log.info("fileName:{}", fileRequest.getFileName());
        //文件大小限制,最大单文件30M
        if(fileRequest.getSize()>31457280){
            throw new BizException(ResultCodeEnum.FILE_SIZE_EXCEED);
        }
        if(fileRequest.getFile().startsWith("base64,")){
            String contentType= ContentInfoUtil.findExtensionMatch(fileRequest.getFileName())==null?
                    "text/plain": ContentInfoUtil.findExtensionMatch(fileRequest.getFileName()).getMimeType();
            fileRequest.setFile("data:"+contentType+";"+fileRequest.getFile());
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String oldFileName = fileRequest.getFileName();
        String suffix=oldFileName.lastIndexOf(".")==-1?"":oldFileName.substring(oldFileName.lastIndexOf("."));
        String newFileName = UUID.randomUUID().toString().replace("-","")+suffix;
        String key = sdf.format(new Date()) + "/" + newFileName;
        // 将文件base64存入缓存
        MultipartFile multipartFile = null;
        try {
            multipartFile = BASE64DecodedMultipartFile.base64ToMultipart(fileRequest.getFile());
        } catch (IOException e) {
            log.error("上传文件失败:",e);
        }
        if (multipartFile == null) {
            throw new BizException("未找到文件");
        }
        String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName");
        S3Util.uploadFile(multipartFile, key,bucketName);
        return key;
    }
    public String uploadFileNoSize(FileRequest fileRequest){
        if (fileRequest == null) {
            throw new BizException(ResultCodeEnum.NO_DATA);
        }
        log.info("fileName:{}", fileRequest.getFileName());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String oldFileName = fileRequest.getFileName();
        String suffix=oldFileName.lastIndexOf(".")==-1?"":oldFileName.substring(oldFileName.lastIndexOf("."));
        String newFileName = UUID.randomUUID().toString().replace("-","")+suffix;
        String key = sdf.format(new Date()) + "/" + newFileName;
        // 将文件base64存入缓存
        MultipartFile multipartFile = null;
        try {
            multipartFile = BASE64DecodedMultipartFile.base64ToMultipart(fileRequest.getFile());
        } catch (IOException e) {
            log.error("上传文件失败:",e);
        }
        if (multipartFile == null) {
            throw new BizException("未找到文件");
        }
        String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName");
        S3Util.uploadFile(multipartFile, key,bucketName);
        return key;
    }
    public void cleanS3File(Collection<String> keyList){
        if (!keyList.isEmpty()){
            String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName");
            S3Util.deleteByKeys(new ArrayList<String>(keyList),bucketName);
        }
    }
    private boolean checkReferer(String referer){
        String[] refererUrls=refererUrl.split(",");
        for (String str : refererUrls) {
            if(str.equalsIgnoreCase(referer)||str.substring(0,str.length()-1).equalsIgnoreCase(referer)){
                return true;
            }
        }
        return false;
    }
    public void preview(HttpServletRequest request,HttpServletResponse response,String key){
        String referer = request.getHeader("Referer");
        if (StrUtil.isBlank(referer)) {
            throw new BizException(ResultCodeEnum.NO_PERMISSION);
        }
        //referer 截取
        String replace = referer.replace("https://", "");
        String newReferer = "https://"+replace.substring(0, replace.indexOf("/")+1);
        if (!checkReferer(newReferer)) {
            throw new BizException(ResultCodeEnum.NO_PERMISSION);
        }
        String fileName = key.substring(key.lastIndexOf("/")+1);
        log.info("文件预览key:{},fileName:{}",key,fileName);
        OutputStream out = null;
        try {
            response.setCharacterEncoding("utf-8");
            String s = ContentInfoUtil.findExtensionMatch(fileName).getMimeType();
            log.info("文件预览key:{},contentType:{}",key,s);
            if (StrUtil.isNotBlank(s)) {
                response.setContentType(s);
            } else{
                response.setContentType(MediaType.ALL_VALUE);
            }
            String agent = request.getHeader("User-Agent");
            if(agent.toLowerCase().contains("firefox")) {
                response.setHeader("Content-Disposition", "inline; filename==?UTF-8?B?" + new String(Base64.encodeBase64(fileName.getBytes("UTF-8")))+"?=");
            }else {
                response.setHeader("Content-Disposition", "inline; filename=" +  new String(fileName.getBytes("gbk"),"iso8859-1"));
            }
            ResponseBytes<GetObjectResponse> objectBytes = S3Util.downloadFile(key,bucketName);
            InputStream inputStream = objectBytes.asInputStream();
            BufferedInputStream bufInputStream = new BufferedInputStream(inputStream);
            out = response.getOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = bufInputStream.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            response.flushBuffer();
            out.close();
        } catch (IOException e) {
            log.error("文件预览失败:",e);
            throw new BizException(ResultCodeEnum.RT_ERROR);
        }catch (S3Exception e) {
            log.error("文件预览失败:",e);
            throw new BizException(e.getMessage());
        }
    }
    public void download(HttpServletRequest request,HttpServletResponse response,String key){
        String referer = request.getHeader("Referer");
        log.info("referer:"+referer);
        if (StrUtil.isBlank(referer)) {
            throw new BizException(ResultCodeEnum.NO_PERMISSION);
        }
        //referer 截取
        String replace = referer.replace("https://", "");
        String newReferer = "https://"+replace.substring(0, replace.indexOf("/")+1);
        if (!checkReferer(newReferer)) {
            throw new BizException(ResultCodeEnum.NO_PERMISSION);
        }
        OutputStream out = null;
        try {
            String fileName = request.getParameter("fileName");
            if (StrUtil.isBlank(fileName)) {
                throw new BizException("fileName不能为空");
            }
            log.info("文件下载key:{},fileName:{}",key,fileName);
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Cache-Control", "No-cache");
            response.setDateHeader("Expires", 0);
            response.setContentType("application/octet-stream");
            String agent = request.getHeader("User-Agent");
            if (null != agent && agent.contains("MSIE") || null != agent && agent.contains("Trident") || null != agent && agent.contains("Edge")) {
                // ie浏览器及Edge浏览器
               fileName = java.net.URLEncoder.encode(fileName, "UTF-8");
            } else if (null != agent && agent.contains("Mozilla")) {
                // 火狐,Chrome等浏览器
                fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            }else {
                // 其它浏览器
                fileName = URLEncoder.encode(fileName, "utf-8");
            }
            response.setHeader("Content-Disposition", "attachment; filename="+fileName);
            ResponseBytes<GetObjectResponse> objectBytes = S3Util.downloadFile(key,bucketName);
            InputStream inputStream = objectBytes.asInputStream();
            BufferedInputStream bufInputStream = new BufferedInputStream(inputStream);
            out = response.getOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = bufInputStream.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            response.flushBuffer();
            out.close();
        } catch (IOException e) {
            log.error("文件下载失败:",e);
            throw new BizException(ResultCodeEnum.RT_ERROR);
        }catch (S3Exception e) {
            log.error("文件下载失败:",e);
            throw new BizException(e.getMessage());
        }
    }
    public void deleteFile(List<String> keys){
        if (CollUtil.isEmpty(keys)) {
            throw new BizException(ResultCodeEnum.NO_DATA);
        }
        String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName");
        S3Util.deleteByKeys(keys,bucketName);
    }
    public void convert(HttpServletRequest request,HttpServletResponse response) {
        //从param参数中获取fileName,如果为空则取当前时间为文件名
        String fileName = request.getParameter("fileName");
        fileName = fileName==null? DateUtils.format(new Date()) : fileName;
        response.setContentType("application/pdf");
        //要保存的文件名
        response.setHeader("Content-Disposition", "inline;filename=" + fileName + ".pdf");
        response.addHeader("Pargam", "no-cache");
        response.addHeader("Cache-Control", "no-cache");
        ServletOutputStream outputStream = null;
        BufferedReader bufferReader=null;
        String base64Str="";
        try {
            if(StringUtils.isNotBlank(request.getParameter("base64Str"))){
                base64Str=request.getParameter("base64Str");
            }else {
                bufferReader = new BufferedReader(request.getReader());
                StringBuilder sb = new StringBuilder();
                String line = null;
                while ((line = bufferReader.readLine()) != null) {
                    sb.append(line);
                }
                String body = sb.toString();
                String[] strings = body.split("&");
                Map<String, String> valueMap = new HashMap<>();
                for (String string : strings) {
                    String[] keyValue = string.split("=", 2);
                    String key = keyValue[0];
                    String value = keyValue[1];
                    valueMap.put(URLDecoder.decode(key, "UTF-8"), URLDecoder.decode(value, "UTF-8"));
                }
                base64Str = valueMap.get("base64Str");
            }
            outputStream = response.getOutputStream();
            byte[] b = DatatypeConverter.parseBase64Binary(base64Str);
            outputStream.write(b, 0, b.length);
        } catch (IOException e) {
            log.error("文件转换异常:",e);
            throw new BizException(ResultCodeEnum.RT_ERROR);
        }finally {
            try {
                if(bufferReader!=null)bufferReader.close();
            } catch (IOException e) {
            }
        }
    }
    public static String getFilecharset(InputStream inputStream) {
        //默认GBK
        String charset = "GBK";
        byte[] first3Bytes = new byte[3];
        try(BufferedInputStream bis = new BufferedInputStream(inputStream)) {
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            // 文件编码为 ANSI
            if (read == -1) {
                return charset;
            }
            // 文件编码为 Unicode
            if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
                return  "UTF-16LE";
            }
            // 文件编码为 Unicode big endian
            if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
                return "UTF-16BE";
            }
            // 文件编码为 UTF-8
            if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF) {
                return "UTF-8";
            }
            bis.reset();
            int loc = 0;
            while ((read = bis.read()) != -1) {
                loc++;
                if (read >= 0xF0) {
                    break;
                }
                // 单独出现BF以下的,也算是GBK
                if (0x80 <= read && read <= 0xBF) {
                    break;
                }
                if (0xC0 <= read && read <= 0xDF) {
                    read = bis.read();
                    // 双字节 (0xC0 - 0xDF)
                    if (0x80 <= read && read <= 0xBF) {
                        // (0x80
                        // - 0xBF),也可能在GB编码内
                        continue;
                    }
                    break;
                }
                // 也有可能出错,但是几率较小
                if (0xE0 <= read && read <= 0xEF) {
                    read = bis.read();
                    if (0x80 <= read && read <= 0xBF) {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            charset = "UTF-8";
                        }
                    }
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return charset;
    }
}
src/main/java/com/deloitte/system/service/LoanerApplicationService.java
New file
@@ -0,0 +1,149 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.core.utils.StringUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.LoanerApplication;
import com.deloitte.system.model.Repair;
import com.deloitte.system.request.ContactSearchDto;
import com.deloitte.system.request.LoanerApplicationDto;
import com.deloitte.system.request.SearchDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class LoanerApplicationService {
    private String tableName = "loaner_application";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public LoanerApplicationDto queryForOne(String dataId) {
        LoanerApplication loanerApplication = LoanerApplication.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(loanerApplication)) {
           LoanerApplicationDto dto =  BeanHelper.copyAs(loanerApplication,LoanerApplicationDto.class);
        return  dto;
        }
        return null;
    }
    public List<LoanerApplicationDto> insertList(List<LoanerApplicationDto> loanerApplicationList, String txId) {
        List<LoanerApplicationDto> respList = new ArrayList<>();
        CacheList<LoanerApplicationDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<LoanerApplicationDto> dtoItemList = new ArrayList<>();
        loanerApplicationList.forEach(e -> {
            String dataId = idWorker.nextId();
            LoanerApplicationDto cacheItem = BeanHelper.copyAs(e, LoanerApplicationDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            LoanerApplicationDto target = BeanHelper.copyAs(e, LoanerApplicationDto.class);
            target.setDataId(dataId);
            LoanerApplicationDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            cryptoService.convertor(dto,target);
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<LoanerApplicationDto> updateList(List<LoanerApplicationDto> loanerApplicationList, String txId) {
        List<LoanerApplicationDto> respList = new ArrayList<>();
        CacheList<LoanerApplicationDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<LoanerApplicationDto> dtoItemList = new ArrayList<>();
        loanerApplicationList.forEach(e -> {
            LoanerApplication loanerApplication = LoanerApplication.dao.findById(e.getDataId());
            LoanerApplicationDto cacheItem = BeanHelper.copyAs(e, LoanerApplicationDto.class);
            dtoItemList.add(cacheItem);
            LoanerApplicationDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,loanerApplication),LoanerApplicationDto.class);
            LoanerApplicationDto target = BeanHelper.copyAs(dto, LoanerApplicationDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setApplyPersonEncrypt(dto.getApplyPerson());
            target.setApplyPersonPhoneEncrypt(dto.getApplyPersonPhone());
            target.setApplicantMailboxEncrypt(dto.getApplicantMailbox());
            target.setLoanerReceiveStaffEncrypt(dto.getLoanerReceiveStaff());
            target.setDirectShippmentAddressEncrypt(dto.getDirectShippmentAddress());
            target.setLoanerReceiveStaffPhoneEncrypt(dto.getLoanerReceiveStaffPhone());
            target.setReturnTrakeStaffEncrypt(dto.getReturnTrakeStaff());
            target.setReturnNumberEncrypt(dto.getReturnNumber());
            target.setPostCodeEncrypt(dto.getPostCode());
            target.setLoanerSerEncrypt(dto.getLoanerSer());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        LoanerApplication loanerApplication = LoanerApplication.dao.findById(dataId);
        loanerApplication.setIsDelete(1);
        return loanerApplication.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        LoanerApplication loanerApplication = LoanerApplication.dao.findById(dataId);
        loanerApplication.setIsDelete(0);
        return loanerApplication.saveOrUpdate();
    }
    public List<LoanerApplicationDto> searchList(SearchDto searchDto) {
        List<String> dataIds = searchDto.getDataIds();
        String contactName = searchDto.getName();
        StringBuilder queryParam = new StringBuilder();
        if (CollectionUtil.isEmpty(dataIds) && StringUtils.isEmpty(contactName)) {
            //参数都为空,直接返回Null
            return null;
        } else {
            if(CollectionUtil.isNotEmpty(dataIds)){
                queryParam.append("and id in").append(dataIds.stream().collect(Collectors.joining(", ", "(", ")")));
            }
            if(StringUtils.isNotEmpty(contactName)) {
                queryParam.append("and apply_person like ").append("\'%").append(contactName).append("%\'");
            }
            List<LoanerApplication> contactList = LoanerApplication.dao.findList(queryParam.toString());
            return contactList.stream()
                    .map(contact -> BeanHelper.copyAs(contact,LoanerApplicationDto.class)).collect(Collectors.toList());
        }
    }
}
src/main/java/com/deloitte/system/service/LoanerUserService.java
New file
@@ -0,0 +1,128 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.LoanerUser;
import com.deloitte.system.request.LoanerUserDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class LoanerUserService {
    private String tableName="loaner_user";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public LoanerUserDto queryForOne(String dataId) {
        LoanerUser loanerUser =LoanerUser.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(loanerUser)){
            LoanerUserDto dto = BeanHelper.copyAs(loanerUser, LoanerUserDto.class);
            return dto;
        }
        return null;
    }
    public List<LoanerUserDto> insertList(List<LoanerUserDto> loanerUserDtoList, String txId) {
        List<LoanerUserDto> respList = new ArrayList<>();
        CacheList<LoanerUserDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<LoanerUserDto> dtoItemList = new ArrayList<>();
        loanerUserDtoList.forEach(e -> {
            String dataId = idWorker.nextId();
            LoanerUserDto cacheItem = BeanHelper.copyAs(e, LoanerUserDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            LoanerUserDto target = BeanHelper.copyAs(e, LoanerUserDto.class);
            target.setDataId(dataId);
            LoanerUserDto dto =  cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setContactEncrypt(dto.getContact());
            target.setContactNumberEncrypt(dto.getContactNumber());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<LoanerUserDto> updateList(List<LoanerUserDto> loanerUserDtoList, String txId) {
        List<LoanerUserDto> respList = new ArrayList<>();
        CacheList<LoanerUserDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<LoanerUserDto> dtoItemList = new ArrayList<>();
        loanerUserDtoList.forEach(e -> {
            LoanerUser loanerUser = LoanerUser.dao.findById(e.getDataId());
            LoanerUserDto cacheItem = BeanHelper.copyAs(e, LoanerUserDto.class);
            dtoItemList.add(cacheItem);
            LoanerUserDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,loanerUser),LoanerUserDto.class);
            LoanerUserDto target = BeanHelper.copyAs(dto, LoanerUserDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setContactEncrypt(dto.getContact());
            target.setContactNumberEncrypt(dto.getContactNumber());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        LoanerUser loanerUser = LoanerUser.dao.findById(dataId);
        loanerUser.setIsDelete(1);
        return loanerUser.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        LoanerUser loanerUser = LoanerUser.dao.findById(dataId);
        loanerUser.setIsDelete(0);
        return loanerUser.saveOrUpdate();
    }
    public List<LoanerUserDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<LoanerUser> loanerUsers = LoanerUser.dao.findList(queryParam.toString());
        return loanerUsers.stream()
                .map(loanerUser -> BeanHelper.copyAs(loanerUser, LoanerUserDto.class)).collect(Collectors.toList());
    }
}
src/main/java/com/deloitte/system/service/MailMergeService.java
New file
@@ -0,0 +1,153 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.MailMerge;
import com.deloitte.system.request.MailMergeDto;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class MailMergeService {
    private String tableName="mail_merge";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public MailMergeDto queryForOne(String dataId) {
        MailMerge mailMerge =MailMerge.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(mailMerge)){
            MailMergeDto dto = BeanHelper.copyAs(mailMerge, MailMergeDto.class);
            return dto;
        }
        return null;
    }
    public List<MailMergeDto> insertList(List<MailMergeDto> mailMergeDtoList, String txId) {
        List<MailMergeDto> respList = new ArrayList<>();
        CacheList<MailMergeDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<MailMergeDto> dtoItemList = new ArrayList<>();
        mailMergeDtoList.forEach(e -> {
            String dataId = idWorker.nextId();
            MailMergeDto cacheItem = BeanHelper.copyAs(e, MailMergeDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            MailMergeDto target = BeanHelper.copyAs(e, MailMergeDto.class);
            target.setDataId(dataId);
            MailMergeDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setCcEncrypt(dto.getCc());
            target.setBccEncrypt(dto.getBcc());
            target.setAuthorEncrypt(dto.getAuthor());
            target.setAllMemberEncrypt(dto.getAllMember());
            target.setToNameEncrypt(dto.getToName());
            target.setAllMemberNameEncrypt(dto.getAllMemberName());
            target.setCcNameEncrypt(dto.getCcName());
            target.setBccNameEncrypt(dto.getBccName());
            target.setRecipientEncrypt(dto.getRecipient());
            target.setPremaryRecipientEncrypt(dto.getPremaryRecipient());
            target.setSendEncrypt(dto.getSend());
            target.setFromEncrypt(dto.getFrom());
            target.setMessageEncrypt(dto.getMessage());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<MailMergeDto> updateList(List<MailMergeDto> mailMergeDtoList, String txId) {
        List<MailMergeDto> respList = new ArrayList<>();
        CacheList<MailMergeDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<MailMergeDto> dtoItemList = new ArrayList<>();
        mailMergeDtoList.forEach(e -> {
            MailMerge mailMerge = MailMerge.dao.findById(e.getDataId());
            MailMergeDto cacheItem = BeanHelper.copyAs(e, MailMergeDto.class);
            dtoItemList.add(cacheItem);
            MailMergeDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,mailMerge),MailMergeDto.class);
            MailMergeDto target = BeanHelper.copyAs(dto, MailMergeDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setCcEncrypt(dto.getCc());
            target.setBccEncrypt(dto.getBcc());
            target.setAuthorEncrypt(dto.getAuthor());
            target.setAllMemberEncrypt(dto.getAllMember());
            target.setToNameEncrypt(dto.getToName());
            target.setAllMemberNameEncrypt(dto.getAllMemberName());
            target.setCcNameEncrypt(dto.getCcName());
            target.setBccNameEncrypt(dto.getBccName());
            target.setRecipientEncrypt(dto.getRecipient());
            target.setPremaryRecipientEncrypt(dto.getPremaryRecipient());
            target.setSendEncrypt(dto.getSend());
            target.setFromEncrypt(dto.getFrom());
            target.setMessageEncrypt(dto.getMessage());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        MailMerge mailMerge = MailMerge.dao.findById(dataId);
        mailMerge.setIsDelete(1);
        return mailMerge.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        MailMerge mailMerge = MailMerge.dao.findById(dataId);
        mailMerge.setIsDelete(0);
        return mailMerge.saveOrUpdate();
    }
    public List<MailMergeDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<MailMerge> quotesList = MailMerge.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, MailMergeDto.class)).collect(Collectors.toList());
    }
}
src/main/java/com/deloitte/system/service/MailService.java
New file
@@ -0,0 +1,889 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.common.aws.util.S3Util;
import com.common.core.domain.DesensitiveType;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.service.SFService;
import com.common.core.utils.*;
import com.deloitte.system.model.MailMerge;
import com.deloitte.system.request.*;
import com.j256.simplemagic.ContentInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.search.FlagTerm;
import javax.mail.util.ByteArrayDataSource;
import javax.transaction.Transactional;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
/**
 * @Author: Ben Pi
 * @Date: 08/02/2022 15:16
 */
@Slf4j
@Service
public class MailService {
    @Value("${aws.s3.bucketName}")
    private String bucketName;
    @Value("${mail.default-encoding}")
    private String defaultEncoding;
    @Value("${mail.smtp.host}")
    private String smtpHost;
    @Value("${mail.smtp.port}")
    private Integer smtpPort;
    @Value("${mail.smtp.protocol}")
    private String smtpProtocol;
    @Value("${mail.imap.host}")
    private String imapHost;
    @Value("${mail.imap.port}")
    private String imapPort;
    @Value("${mail.imap.protocol}")
    private String imapProtocol;
    @Value("${salesforce.baseUrl}")
    private String sfUrl;
    @Value("${file.url}")
    private String fileUrl;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    FileService fileService;
    @Autowired
    private SFService sfService;
    public String sendEmail(String txId,MessageVo messageVo) {
        try {
            JavaMailSender mailSender=getMailSender();
            log.info("开始发送邮件:from["+messageVo.getFrom()+"]"+"subject["+messageVo.getSubjectCopy()+"]");
            MimeMessage message = mailSender.createMimeMessage();
            generateMessage(message,messageVo);
            // 更新notSaveEmail状态为true
            updateSFRecord(messageVo.getRecordType(),messageVo.getRecordId());
            // 处理邮件数据,发送sf mailMerge
            Map<String,String> idMap=handleEmail(message,messageVo.getRecordId(),messageVo.getRecordType().substring(0,1).toUpperCase(),true,messageVo.getFileMap());
            //发送邮件
            mailSender.send(message);
            log.info("结束发送邮件:from["+messageVo.getFrom()+"]"+"subject["+messageVo.getSubjectCopy()+"]");
            // 返回sf mailMerge id
            return idMap.get("mailMergeId");
        } catch (Exception e) {
            log.error("发送邮件失败:",e);
            throw new BizException(ResultCodeEnum.EMAIL_ERROR);
        }
    }
    private JavaMailSender getMailSender(){
        this.getInit();
        JavaMailSenderImpl sender = new JavaMailSenderImpl();
        sender.setHost(smtpHost);
        sender.setDefaultEncoding(defaultEncoding);
        sender.setPort(smtpPort);
        sender.setProtocol(smtpProtocol);
        sender.setUsername(username);
        sender.setPassword(password);
        Properties p = new Properties();
        p.setProperty("mail.smtp.auth", "true");
        p.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        p.setProperty("mail.smtp.socketFactory.fallback", "false");
        p.setProperty("mail.smtp.socketFactory.port", smtpPort.toString());
        sender.setJavaMailProperties(p);
        return sender;
    }
    /**
     * 更新邮件notSaveEmail状态
     *
     * @param recordType
     * @param recordId
     */
    private void updateSFRecord(String recordType, String recordId) {
        //更新SWO或其他表的 notSaveEmail 标识符
        JSONObject jsonObject = new JSONObject();
        //标记跳过邮件回复监听
        jsonObject.put("notSaveEmail__c",true);
        String tableName = "";
        if("SWO".equalsIgnoreCase(recordType)){
            tableName = "SWO__c";
        }
        if("Case".equalsIgnoreCase(recordType)){
            tableName = "User_FaultInfo__c";
        }
        if("Quotes".equals(recordType)){
            String sql="select Id,Name,notSaveEmail__c,SWO__c,SWO__r.Quotation_send__c from Quotes__c where Id= '"+recordId+"'";
            JSONObject quotes=sfService.querySFData(sql).getJSONObject(0);
            tableName = "Quotes__c";
            if (StringUtils.isNotBlank(quotes.getString("SWO__c")) && quotes.get("SWO__r.Quotation_send__c") == null) {
                JSONObject swo = new JSONObject();
                String swoRecordId = quotes.getString("SWO__c");
                swo.put("Quotation_send__c",new Date());
                // update swo;
                sfService.updateSFData("SWO__c",swoRecordId,swo);
            }
        }
        sfService.updateSFData(tableName,recordId,jsonObject);
    }
    @Autowired
    private Environment env;
    @Autowired
    private SecretsManagerUtils secretsManagerUtils;
    public void receiveImapMail() {
        try {
            this.getInit();
            // 准备连接服务器的会话信息
            Properties props = new Properties();
            props.setProperty("mail.store.protocol", imapProtocol);
            props.setProperty("mail.imap.host", imapHost);
            props.setProperty("mail.imap.port", imapPort);
            props.setProperty("mail.imap.ssl.enable", "true");
            props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            // 创建Session实例对象
            Session session = Session.getDefaultInstance(props, new Authenticator(){
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });
            session.setDebug(false);
            // 连接邮件服务器
            Store store = session.getStore(imapProtocol);
            store.connect(imapHost,Integer.valueOf(imapPort), username, password);
            Folder folder = store.getFolder("INBOX");
            folder.open(Folder.READ_WRITE);
            // 获得收件箱的邮件列表
            FlagTerm ft = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
            Message[] messages = folder.search(ft);
            log.info("------------------------开始解析邮件----------------------------------");
            log.info("-----------------您的邮箱共有新邮件:" + messages.length + " 封--------------");
            for (int i = 0; i < messages.length; i++) {
                try {
                    Message msg = messages[i];
                    //业务处理 1.写入mail merge 2.存储附件
                    processReceiveEmail(msg);
                    // 邮件在处理完成后更新邮件状态为已读
                    msg.setFlag(Flags.Flag.SEEN, true);
                }catch (Exception e){
                    log.error("同步邮件失败:",e);
                }
            }
            log.info("----------------End------------------");
            // 关闭资源
            folder.close(false);
            store.close();
        }catch (Exception e){
            log.error("同步邮件失败:",e);
        }
    }
    /**
     * 接收邮件处理
     *
     * @param msg
     * @return 返回当前邮件是否需要接收标识(需要接收:true,不需要接收:false)
     * @throws Exception
     */
    private void processReceiveEmail(Message msg) throws Exception {
        log.info("接收邮件:" + msg.getSubject());
        if(StringUtils.isEmpty(msg.getSubject())){
            return;
        }
        //[EXT]Sandbox: 回复:h45h54h54~SWO:Test By Bubba
        String[] str = msg.getSubject().split("~");
        if(str.length < 2){
            log.info("当前邮件不做处理:{}",msg.getSubject());
            return;
        }
        String type= null;
        String recordId= null;
        //SWO:Test By Bubba
        String nameAll = str[1];
        //S
        type = nameAll.substring(0,1);
        String tableName = type.equalsIgnoreCase("s") ? "SWO__c":
                (type.equalsIgnoreCase("c") ? "User_FaultInfo__c": (type.equalsIgnoreCase("q") ? "Quotes__c" : ""));
        //Test By Bubba
        String name = nameAll.substring(nameAll.indexOf(":")+1);
        if(StringUtils.isEmpty(tableName)||StringUtils.isEmpty(name)){
            log.info("解析tableName或name为空,subject:{}",msg.getSubject());
            return;
        }
        String sql="select Id,notSaveEmail__c,Name from "+tableName+" where Name= '"+name+"'";
        JSONArray jsonArray=sfService.querySFData(sql);
        if(jsonArray==null || jsonArray.size()==0){
            log.info("邮件:{},查询notSaveEmail结果为空,tableName:{},name:{}",msg.getSubject(),tableName,name);
            return;
        }
        recordId = jsonArray.getJSONObject(0).getString("Id");
        if( jsonArray.getJSONObject(0).getBoolean("notSaveEmail__c")){
            log.info("跳过邮件服务监听:" + msg.getSubject()+","+recordId);
            // 更新notSaveEmail__c标识
            JSONObject jsonObject = new JSONObject();
            //标记跳过邮件回复监听
            jsonObject.put("notSaveEmail__c",false);
            sfService.updateSFData(tableName,recordId,jsonObject);
            // 此处为自己抄送给自己的邮件,设置为已读
            return;
        }
        handleEmail(msg, recordId, type,false,null);
    }
    /**
     * 处理email方法
     *
     * @param msg 邮件对象
     * @param recordId 关联id eg:swo_id
     * @param type 邮件类型 eg:swo
     * @param isSend 是否为发出的邮件(发出:true,接收:false)
     * @param fileMap 附件信息(key:s3文件Key,value:fileName)
     * @return
     * @throws Exception
     */
    @Transactional
    public Map<String, String> handleEmail(Message msg, String recordId, String type, boolean isSend, Map<String, String> fileMap) throws MessagingException {
        log.info("------------- save email -------------");
        try {
            //保存邮件
            MailMerge mailMerge = saveMailMerge(msg, recordId, type, isSend);
            //接收到的邮件->保存附件到s3
            if (!isSend) {
                //解析到邮件中的附件,key为附件,value为filename
                Map<BodyPart,String> parsedFileMap = new HashMap<>();
                parseAttachMent(msg, parsedFileMap);
                //遍历保存附件文件到s3
                for (BodyPart bodyPart : parsedFileMap.keySet()) {
                    String fileName = parsedFileMap.get(bodyPart);
                    String key = saveFile(fileName,bodyPart);
                    fileMap = new HashMap<>();
                    fileMap.put(key,fileName);
                }
            }
            //推送邮件和附件信息到SF  idMap -> key:mailMergeId/fileAddressId,value:salesforce保存后返回的id,如果有多个附件,id用","分隔
            Map<String, String> idMap = postMailToSF(mailMerge, fileMap);
            // 回写sfid
            updateMailMerge(idMap,mailMerge.getDataId());
            return idMap;
        } catch (Exception e) {
            log.error("保存邮件异常,{}",e.getMessage(),e);
            // 修改Message状态为未读
            msg.setFlag(Flags.Flag.SEEN,false);
            throw new BizException("保存邮件异常");
        }
    }
    /**
     * 保存mailmerge
     *
     * @param msg 邮件对象
     * @param recordId 关联id eg:swo_id
     * @param type 邮件类型 eg:swo
     * @param isSend 是否为发出的邮件(发出:true,接收:false)
     * @return
     * @throws Exception
     */
    private MailMerge saveMailMerge(Message msg, String recordId, String type,boolean isSend) throws Exception {
        MailMerge mailMerge = new MailMerge();
        String dataId = idWorker.nextId();
        mailMerge.setDataId(Long.parseLong(dataId));
        mailMerge.setRecord(recordId);
        //邮件标题
        if(StringUtils.isNotBlank(msg.getSubject())){
            mailMerge.setSubjectCopy(msg.getSubject());
            String[] str = msg.getSubject().split("~");
            mailMerge.setSubject(str[0]);
            mailMerge.setName(str[0]);
        }else{
            mailMerge.setSubjectCopy("");
            mailMerge.setSubject("");
            mailMerge.setName("");
        }
        if("S".equals(type)){
            mailMerge.setSwo(recordId);
            mailMerge.setRecordType("SWO");
        }
        if("C".equals(type)){
            mailMerge.setCaseF(recordId);
            mailMerge.setRecordType("Case");
        }
        if("Q".equals(type)){
            mailMerge.setQuotes(recordId);
            mailMerge.setRecordType("Quotes");
        }
        //发件人地址
        InternetAddress address[];
        if(isSend) {
            address = (InternetAddress[]) msg.getReplyTo();
        }else{
            address = (InternetAddress[]) msg.getFrom();
        }
        String author = (address!=null && address.length>0) ?address[0].getAddress():"";
        mailMerge.setFrom(author);
        mailMerge.setAuthor(author);
        //收件人地址
        mailMerge.setRecipient(getMailAddress(msg,"TO"));
        //抄送人
        mailMerge.setCc(getMailAddress(msg,"CC"));
        //秘抄送人
        mailMerge.setBcc(getMailAddress(msg,"BCC"));
        setAllMember(mailMerge,getMailAddressMap(msg,"TO"),getMailAddressMap(msg,"CC"),getMailAddressMap(msg,"BCC"));
        //邮件信息
        StringBuffer bodytext = new StringBuffer();
        Multipart multipart = (msg.getContent() instanceof String) ? null : (Multipart) msg.getContent();
        String mailContent;
        if(multipart==null){
            mailContent=msg.getContent().toString();
        }else {
            int counts = multipart.getCount();
            for (int i = 0; i < counts; i++) {
                getMailContent(multipart.getBodyPart(i), bodytext);
            }
            mailContent = bodytext.toString();
        }
        mailMerge.setMessage(mailContent);
        //收取回复邮件时间
        mailMerge.setDate(new Date());
        if(isSend){
            mailMerge.setType("send");
        }else {
            mailMerge.setType("reply");
        }
        mailMerge.setEmailSent("YES");
        log.info("保存mail merge:{}",mailMerge.toString());
        mailMerge.save();
        return mailMerge;
    }
    /**
     * 更新sf返回的mailMergeId和fileAddressId到mail_merge表
     *
     * @param idMap
     */
    public void updateMailMerge(Map<String,String> idMap,Long dataId){
        MailMerge mailMerge = new MailMerge();
        mailMerge.setDataId(dataId);
        String mailMergeId = idMap.get("mailMergeId");
        String fileAddressId = idMap.get("fileAddressId");
        mailMerge.setSfRecordId(mailMergeId);
        mailMerge.setSfFileAddressId(fileAddressId);
        log.info("update mail_merge---------->id:{},mailMergeId:{},fileAddressId:{}",dataId,mailMergeId,fileAddressId);
        mailMerge.update();
    }
    /**
     * 解析附件
     */
    private void parseAttachMent(Part part,Map fileMap) throws Exception {
        String fileName = "";
        if (part.isMimeType("multipart/*")) {
            Multipart mp = (Multipart) part.getContent();
            for (int i = 0; i < mp.getCount(); i++) {
                BodyPart mpart = mp.getBodyPart(i);
                String disposition = mpart.getDisposition();
                if ((disposition != null)
                        && ((disposition.equals(Part.ATTACHMENT)) || (disposition
                        .equals(Part.INLINE)))) {
                    fileName = mpart.getFileName();
                    if(StringUtils.isBlank(fileName) && "message/rfc822".equalsIgnoreCase(mpart.getHeader("Content-Type")[0])){
                        Properties props = new Properties();
                        Session session = Session.getInstance(props, null);
                        MimeMessage msg=new MimeMessage(session,mpart.getInputStream());
                        fileName=msg.getSubject()+".eml";
                    }
                    fileName = MimeUtility.decodeText(fileName);
                    //附件写入map
                    fileMap.put(mpart,fileName);
                } else if (mpart.isMimeType("multipart/*")) {
                    parseAttachMent(mpart,fileMap);
                } else {
                    fileName = mpart.getFileName();
                    if (fileName != null) {
                        fileName = MimeUtility.decodeText(fileName);
                        fileMap.put(mpart,fileName);
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            parseAttachMent((Part) part.getContent(),fileMap);
        }
    }
    /**
     * 保存文件到s3
     */
    private String saveFile(String fileName,BodyPart bodyPart) throws IOException, MessagingException {
        log.info("保存附件:"+fileName);
        FileRequest fileRequest = new FileRequest();
        fileRequest.setFileName(fileName);
        String contentType= ContentInfoUtil.findExtensionMatch(fileName)==null?"text/plain": ContentInfoUtil.findExtensionMatch(fileName).getMimeType();
        String basePrefix="data:"+contentType+";base64,";
        int size = bodyPart.getSize();
        byte [] buffer = new byte[size];
        InputStream instream;
        instream = bodyPart.getInputStream();
        instream.read(buffer,0,size);
        fileRequest.setFile(basePrefix+ Base64.getEncoder().encodeToString(buffer));
        String key = fileService.uploadFileNoSize(fileRequest);
        log.info("附件上传至s3完成,key:"+key);
        return key;
    }
    /**
     * 获得邮件的收件人,抄送,和密送的地址,根据所传递的参数的不同 "to"----收件人 "cc"---抄送人地址 "bcc"---密送人地址
     */
    public String getMailAddress(Message msg,String type) throws Exception {
        StringBuilder mailaddr = new StringBuilder();
        Map<String, String> mailAddressMap = getMailAddressMap(msg, type);
        Set<String> addressSet = mailAddressMap.keySet();
        for (String address:addressSet) {
            mailaddr.append(address).append(";");
        }
        return mailaddr.toString();
    }
    /**
     * 获得邮件的收件人,抄送,和密送的地址,根据所传递的参数的不同 "to"----收件人 "cc"---抄送人地址 "bcc"---密送人地址
     *
     * @return key:邮件地址,value:发送人名称
     */
    public Map<String,String> getMailAddressMap(Message msg,String type) throws Exception {
        HashMap<String,String> addressMap = new HashMap<>();
        String addtype = type.toUpperCase();
        InternetAddress[] address = null;
        if ("TO".equals(addtype) || "CC".equals(addtype)|| "BCC".equals(addtype)) {
            if ("TO".equals(addtype)) {
                address = (InternetAddress[]) msg.getRecipients(Message.RecipientType.TO);
            } else if ("CC".equals(addtype)) {
                address = (InternetAddress[]) msg.getRecipients(Message.RecipientType.CC);
            } else {
                address = (InternetAddress[]) msg.getRecipients(Message.RecipientType.BCC);
            }
            if (address != null) {
                for (int i = 0; i < address.length; i++) {
                    String email = address[i].getAddress();
                    if (email == null) {
                        email = "";
                    } else {
                        email = MimeUtility.decodeText(email);
                    }
                    String personal = address[i].getPersonal();
                    if (personal == null) {
                        personal = "";
                    } else {
                        personal = MimeUtility.decodeText(personal);
                    }
                    addressMap.put(email,personal);
                }
            }
        } else {
            throw new Exception("Error emailaddr type!");
        }
        return addressMap;
    }
    /**
     * 解析邮件,把得到的邮件内容保存到一个StringBuffer对象中,解析邮件 根据MimeType类型的不同执行不同的操作
     */
    public String getMailContent(Part part,StringBuffer bodytext) throws Exception {
        String contenttype = part.getContentType();
        int nameindex = contenttype.indexOf("name");
        boolean conname = false;
        if (nameindex != -1) {
            conname = true;
        }
        if (part.isMimeType("text/plain") && !conname) {
            if (part.getContent() instanceof InputStream) {
                return null;
            }
            // 只保存文本内容
            bodytext.append((String) part.getContent());
            log.info("添加邮件文本:{}",part.getContent().toString());
        } else if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int counts = multipart.getCount();
            for (int i = 0; i < counts; i++) {
                getMailContent(multipart.getBodyPart(i),bodytext);
            }
        } else if (part.isMimeType("message/rfc822")) {
            getMailContent((Part) part.getContent(),bodytext);
        } else {
            //其他类型暂不处理
        }
        return bodytext.toString();
    }
    private void setAllMember(MailMerge mailMerge,Map<String,String> toAddress,Map<String,String> ccAddresses,Map<String,String> bccAddresses) {
        mailMerge.setAllMember("");
        mailMerge.setAllMemberName("");
        mailMerge.setAllMemberType("");
        if(toAddress !=null && toAddress.keySet().size()!=0){
            for(String address : toAddress.keySet()){
                //邮件地址之间用“;”隔开,发送邮件页会用“;”分割字符串
                mailMerge.setAllMember(mailMerge.getAllMember() + address + ";");
                //名字暂时都用“-”,邮件页面分割出“-”会用空代替
                mailMerge.setAllMemberName(mailMerge.getAllMemberName() + toAddress.get(address)+ ";");
                mailMerge.setAllMemberType(mailMerge.getAllMemberType() + "to;");
            }
        }
        if(ccAddresses !=null && ccAddresses.keySet().size()!=0){
            for(String address : ccAddresses.keySet()){
                if(address.startsWith("email@") || "".equals(address)){
                    continue;
                }
                //邮件地址之间用“;”隔开,发送邮件页会用“;”分割字符串
                mailMerge.setAllMember(mailMerge.getAllMember() + address + ';');
                //名字暂时都用“-”,邮件页面分割出“-”会用空代替
                mailMerge.setAllMemberName(mailMerge.getAllMemberName() + "-;");
                mailMerge.setAllMemberType(mailMerge.getAllMemberType() + "cc;");
            }
        }
        if(bccAddresses !=null && bccAddresses.keySet().size()!=0){
            for(String address : bccAddresses.keySet()){
                if(address.startsWith("email@") || "".equals(address)){
                    continue;
                }
                //邮件地址之间用“;”隔开,发送邮件页会用“;”分割字符串
                mailMerge.setAllMember(mailMerge.getAllMember() + address + ';');
                //名字暂时都用“-”,邮件页面分割出“-”会用空代替
                mailMerge.setAllMemberName(mailMerge.getAllMemberName() + "-;");
                mailMerge.setAllMemberType(mailMerge.getAllMemberType() + "bcc;");
            }
        }
    }
    /**
     * 调用salesforce 邮件保存接口
     *
     * @return Map<String,String> key:mailMergeId/fileAddressId,value:salesforce保存后返回的id,如果有多个附件,id用","分隔
     */
    public Map<String,String> postMailToSF(MailMerge mailMerge,Map<String,String> fileMap){
        HashMap<String, String> idMap = new HashMap<>();
        SfCompositeRequest request = new SfCompositeRequest();
        //mailMerge to SFMessageVo
        SFMessageVo sfMessageVo = mailMergeToSFMessageVo(mailMerge);
        request.addMailMerge(sfMessageVo);
        // fileMap to SFFileAddressVo
        if (fileMap!=null && fileMap.size()>0) {
            List<SFFileAddressVo> sfFileAddressVos = fileMapToSFFileAddressVo(fileMap);
            request.addFileAddress(sfFileAddressVos);
        }
        SFTokenDto tokenDto = sfService.getToken();
        String accessToken = tokenDto.getAccessToken();
        HttpHeaders header = new HttpHeaders();
        header.add("Authorization", "Bearer " + accessToken);
        header.add("Content-Type","application/json");
        try {
            String requestBody = request.toJsonString();
            log.info("推送sf邮件接口,request:{}",requestBody);
            ResponseEntity sfRes = RestUtil.postNative(sfUrl + "services/data/v53.0/composite", header, null, request.toJsonString());
            log.info("推送sf邮件接口,response:{}",Objects.requireNonNull(sfRes.getBody()).toString());
            int statusCodeValue = sfRes.getStatusCodeValue();
            JSONObject body = (JSONObject)sfRes.getBody();
            JSONArray responseBody = body.getJSONArray("compositeResponse");
            responseBody.forEach(object -> {
                LinkedHashMap map = (LinkedHashMap) object;
                // 判断返回状态
                if(!map.get("httpStatusCode").toString().startsWith("2")){
                    log.error("推送salesforce邮件接口返回异常,response:"+sfRes.getBody());
                    throw new BizException("推送salesforce邮件接口返回状态异常");
                }
                // 获取返回参数中的mailMergeId
                if ("NewMailMerge".equals(map.get("referenceId"))) {
                    LinkedHashMap body1 = (LinkedHashMap) map.get("body");
                    String mailMergeId = body1.get("id").toString();
                    idMap.put("mailMergeId",mailMergeId);
                }
                // 获取返回参数中的NewFileAddress,如果是多个,用','拼接
                StringBuilder fileAddressIdBuilder = new StringBuilder();
                if (map.get("referenceId").toString().startsWith("NewFileAddress")) {
                    LinkedHashMap body1 = (LinkedHashMap) map.get("body");
                    String fileAddressId = body1.get("id").toString();
                    fileAddressIdBuilder.append(",").append(fileAddressId);
                }
                if(fileAddressIdBuilder.length()>0){
                    String substring = fileAddressIdBuilder.substring(1);
                    idMap.put("fileAddressId",substring);
                }
            });
            if(!Integer.toString(statusCodeValue).startsWith("2")){
                throw new BizException("请求SF服务保存邮件异常,response code:"+statusCodeValue);
            }
        } catch (Exception e) {
            log.error("请求SF服务异常",e);
            throw new BizException("请求SF服务保存邮件异常");
        }
        return idMap;
    }
    //mailMerge to SFMessageVo
    public SFMessageVo mailMergeToSFMessageVo(MailMerge mailMerge){
        SFMessageVo sfMessageVo = new SFMessageVo();
        sfMessageVo.setName(mailMerge.getName());
        Map<String, String> desensitiveAllMemberNameMap = parseStringToDesensitive(mailMerge.getAllMemberName(),DesensitiveType.CHINESE_NAME);
        sfMessageVo.setALL_MEMBER_NAME__c(desensitiveAllMemberNameMap.get("desensitive"));
        sfMessageVo.setALL_MEMBER_NAME_Encrypted__c(desensitiveAllMemberNameMap.get("encrypt"));
        sfMessageVo.setALL_MEMBER_TYPE__c(mailMerge.getAllMemberType());
        Map<String, String> desensitiveAllMemberMap = parseStringToDesensitive(mailMerge.getAllMember(),DesensitiveType.EMAIL);
        sfMessageVo.setALL_MEMBER__c(desensitiveAllMemberMap.get("desensitive"));
        sfMessageVo.setALL_MEMBER_Encrypted__c(desensitiveAllMemberMap.get("encrypt"));
        Map<String, String> desensitiveCcMap = parseStringToDesensitive(mailMerge.getCc(),DesensitiveType.EMAIL);
        sfMessageVo.setCC__c(desensitiveCcMap.get("desensitive"));
        sfMessageVo.setCC_Encrypted__c(desensitiveCcMap.get("encrypt"));
        Map<String, String> desensitiveBccMap = parseStringToDesensitive(mailMerge.getBcc(),DesensitiveType.EMAIL);
        sfMessageVo.setBCC__c(desensitiveBccMap.get("desensitive"));
        sfMessageVo.setBCC_Encrypted__c(desensitiveBccMap.get("encrypt"));
        Map<String, String> desensitiveFromMap = parseStringToDesensitive(mailMerge.getFrom(),DesensitiveType.EMAIL);
        sfMessageVo.setFROM__c(desensitiveFromMap.get("desensitive"));
        sfMessageVo.setFROM_Encrypted__c(desensitiveFromMap.get("encrypt"));
        sfMessageVo.setMESSAGE__c(mailMerge.getMessage());
        Map<String, String> desensitiveRecipientMap = parseStringToDesensitive(mailMerge.getRecipient(),DesensitiveType.EMAIL);
        sfMessageVo.setRECIPIENT__c(desensitiveRecipientMap.get("desensitive"));
        sfMessageVo.setRECIPIENT_Encrypted__c(desensitiveRecipientMap.get("encrypt"));
        sfMessageVo.setRECORD_TYPE__c(mailMerge.getRecordType());
        sfMessageVo.setSUBJECTCOPY__c(mailMerge.getSubjectCopy());
        sfMessageVo.setSUBJECT__c(mailMerge.getSubject());
        sfMessageVo.setTYPE__c(mailMerge.getType());
        Map<String, String> desensitiveBccNameMap = parseStringToDesensitive(mailMerge.getBccName(),DesensitiveType.CHINESE_NAME);
        sfMessageVo.setBccName__c(desensitiveBccNameMap.get("desensitive"));
        sfMessageVo.setBccName_Encrypted__c(desensitiveBccNameMap.get("encrypt"));
        Map<String, String> desensitiveCcNameMap = parseStringToDesensitive(mailMerge.getCcName(),DesensitiveType.CHINESE_NAME);
        sfMessageVo.setCcName__c(desensitiveCcNameMap.get("desensitive"));
        sfMessageVo.setCcName_Encrypted__c(desensitiveCcNameMap.get("encrypt"));
        Map<String, String> desensitiveToNameMap = parseStringToDesensitive(mailMerge.getToName(),DesensitiveType.CHINESE_NAME);
        sfMessageVo.setToName__c(desensitiveToNameMap.get("desensitive"));
        sfMessageVo.setToName_Encrypted__c(desensitiveToNameMap.get("encrypt"));
        sfMessageVo.setAWS_Data_Id__c(mailMerge.getDataId().toString());
        sfMessageVo.setDATE__c(mailMerge.getDate()==null?null:DateUtils.formatToUtc(mailMerge.getDate(),DateUtils.DATE_TIME_GL_ST_FORMAT_ZT_1));
        sfMessageVo.setRECORD__c(mailMerge.getRecord());
        sfMessageVo.setRECORD_TYPE__c(mailMerge.getRecordType());
        sfMessageVo.setSWO__c(mailMerge.getSwo());
        sfMessageVo.setCaseF__c(mailMerge.getCaseF());
        sfMessageVo.setQuotes__c(mailMerge.getQuotes());
        sfMessageVo.setEMAIL_SENT__c(mailMerge.getEmailSent());
        return sfMessageVo;
    }
    //fileMap to SFFileAddressVo
    private List<SFFileAddressVo> fileMapToSFFileAddressVo(Map<String, String> fileMap){
        ArrayList<SFFileAddressVo> sfFileAddressVos = new ArrayList<>();
        for (String fileKey:fileMap.keySet()) {
            SFFileAddressVo sfFileAddressVo = new SFFileAddressVo();
            String fileName = fileMap.get(fileKey);
            sfFileAddressVo.setAWS_File_Key__c(fileKey);
            sfFileAddressVo.setFileName__c(fileName);
            sfFileAddressVo.setParentRecordId__c("@{NewMailMerge.id}");
            sfFileAddressVo.setDownloadLink__c(fileUrl+"download?key="+fileKey+"&fileName="+fileName);
            sfFileAddressVo.setViewLink__c(fileUrl+"preview?key="+fileKey);
            sfFileAddressVos.add(sfFileAddressVo);
        }
        return sfFileAddressVos;
    }
    /**
     * 将String转换为脱敏字符串和加密字符串
     *
     * @param s
     * @return
     */
    private static Map<String,String> parseStringToDesensitive(String s,DesensitiveType desensitiveType){
        HashMap<String, String> map = new HashMap<>();
        if("".equals(s) || s==null){
            map.put("desensitive",s);
            map.put("encrypt",s);
            return map;
        }
        // 脱敏内容
        StringBuilder desensitiveS = new StringBuilder();
        // 加密内容
        StringBuilder encryptS = new StringBuilder();
        //twtyypmb;twtyypmb66;-;-;
        String[] split = s.split(";");
        for (String name:split) {
            if(!"-".equals(name)){
                desensitiveS.append(DesensitiveUtils.getFieldDesensitive(desensitiveType)).append(";");
                String encryptStr = SM4Utils.encryptStr(name);
                encryptS.append(encryptStr).append(";");
            }else {
                desensitiveS.append(name).append(";");
                encryptS.append(name).append(";");
            }
        }
        // 判断是否需要加;
        if(!s.endsWith(";") && s.length()>0){
            desensitiveS.delete(desensitiveS.length()-1,desensitiveS.length());
            encryptS.delete(encryptS.length()-1,encryptS.length());
        }
        map.put("desensitive",desensitiveS.toString());
        map.put("encrypt",encryptS.toString());
        return map;
    }
    private void generateMessage(MimeMessage message,MessageVo messageVo) throws Exception {
        this.getInit();
        message.setSubject(messageVo.getSubjectCopy(), "UTF-8");
        message.setFrom(new InternetAddress(username));
        if(!ObjectUtils.isEmpty(messageVo.getFrom())) {
            message.addHeader("Sender", messageVo.getFrom());
            message.addHeader("Return-Path", messageVo.getFrom());
            message.setReplyTo(new InternetAddress[]{new InternetAddress(messageVo.getFrom(), messageVo.getFromName()),new InternetAddress(username, "-")});
//            message.addHeader("In-Reply-To", messageVo.getFrom());
//            message.addHeader("Reply-To", messageVo.getFrom());
        }
        if(StringUtils.isNotEmpty(messageVo.getRecipient())) {
            String[] addressList=messageVo.getRecipient().split(";");
            String[] nameList=messageVo.getToName().split(";");
            for (int i = 0; i < addressList.length; i++) {
                try {
                    message.addRecipient(Message.RecipientType.TO, new InternetAddress(addressList[i],nameList[i]));
                } catch (Exception e) {
                    log.error("添加收件人失败,from:"+messageVo.getFrom()+",to:"+addressList[i]+",subject:"+messageVo.getSubjectCopy());
                    log.error("添加收件人失败",e);
                }
            }
        }
        if(StringUtils.isNotEmpty(messageVo.getRecipient())) {
            // 添加监听邮件
            String cc = messageVo.getCc();
            String ccName = messageVo.getCcName();
            if(cc==null || "".equals(cc)){
                cc = username;
                ccName = username.substring(0,username.indexOf("@"));
            }else if(!cc.contains(username)){
                // cc列表中没有olympus公邮,将公邮添加到cc中
                cc += ";"+username;
                ccName += ";"+username.substring(0,username.indexOf("@"));
            }
            String[] addressList=cc.split(";");
            String[] nameList=ccName.split(";");
            for (int i = 0; i < addressList.length; i++) {
                try {
                    message.addRecipient(Message.RecipientType.CC, new InternetAddress(addressList[i],nameList[i]));
                } catch (Exception e) {
                    log.error("添加CC收件人失败,from:"+messageVo.getFrom()+",cc:"+addressList[i]+",subject:"+messageVo.getSubjectCopy());
                    log.error("添加收件人失败",e);
                }
            }
        }
        if(StringUtils.isNotEmpty(messageVo.getBcc())) {
            String[] addressList=messageVo.getBcc().split(";");
            String[] nameList=messageVo.getBccName().split(";");
            for (int i = 0; i < addressList.length; i++) {
                try {
                    message.addRecipient(Message.RecipientType.BCC, new InternetAddress(addressList[i],nameList[i]));
                } catch (Exception e) {
                    log.error("添加收件人失败,from:"+messageVo.getFrom()+",bcc:"+addressList[i]+",subject:"+messageVo.getSubjectCopy());
                    log.error("添加收件人失败",e);
                }
            }
        }
        // Create a wrapper for the HTML and text parts
        MimeBodyPart wrap = new MimeBodyPart();
        // Add the child container to the wrapper object
        wrap.setContent(messageVo.getMessage(), "text/plain; charset=UTF-8");
        // Create a multipart/mixed parent container
        MimeMultipart msg = new MimeMultipart("mixed");
        // Add the multipart/alternative part to the message
        msg.addBodyPart(wrap);
        // Define the attachment
        if(CollectionUtil.isNotEmpty(messageVo.getFileMap())){
            messageVo.getFileMap().forEach((fileKey,fileName) ->{
                try {
                    ResponseBytes<GetObjectResponse> objectBytes = S3Util.downloadFile(fileKey, bucketName);
                    byte[] fileContent = objectBytes.asByteArray();
                    MimeBodyPart att = new MimeBodyPart();
                    DataSource fds = new ByteArrayDataSource(fileContent, ContentInfoUtil.findExtensionMatch(fileName).getMimeType());
                    att.setDataHandler(new DataHandler(fds));
                    att.setFileName(fileName);
                    // Add the attachment to the message.
                    MimeBodyPart attachFileBodypart = new MimeBodyPart();
                    MimeMultipart attachFileMMP = new MimeMultipart("related");
                    attachFileMMP.addBodyPart(att);
                    attachFileBodypart.setContent(attachFileMMP,"text/html;charset=UTF-8");
                    msg.addBodyPart(attachFileBodypart);
                }catch (Exception e){
                    log.error("添加附件失败,from:"+messageVo.getFrom()+",to:"+messageVo.getRecipient()+",subject:"+messageVo.getSubjectCopy()+",fileName:"+fileName+",fileKey:"+fileKey);
                    log.error("添加附件失败:",e);
                }
            });
        }
        // Add the parent container to the message
        message.setContent(msg);
        message.saveChanges();
    }
    private String username = "";
    private String password = "";
    private void getInit(){
        if(StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            JSONObject object = secretsManagerUtils.getSecret(env.getProperty("aws.secrets.mailauth"));
            username = object.getString("mail_username");
            password = object.getString("mail_password");
        }
    }
}
src/main/java/com/deloitte/system/service/OpportunityService.java
New file
@@ -0,0 +1,190 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.*;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.Opportunity;
import com.deloitte.system.model.Quotes;
import com.deloitte.system.request.OpportunityDto;
import com.deloitte.system.request.QuotesDto;
import com.jfinal.plugin.activerecord.Db;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class OpportunityService {
    private String tableName = "opportunity";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public OpportunityDto queryForOne(String dataId) {
        Opportunity opportunity = Opportunity.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(opportunity)) {
            return BeanHelper.copyAs(opportunity,OpportunityDto.class);
        }
        return null;
    }
    public List<OpportunityDto> insertList(List<OpportunityDto> opportunityList, String txId) {
        List<OpportunityDto> respList = new ArrayList<>();
        CacheList<OpportunityDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<OpportunityDto> dtoItemList = new ArrayList<>();
        opportunityList.forEach(e -> {
            String dataId = idWorker.nextId();
            OpportunityDto cacheItem = BeanHelper.copyAs(e, OpportunityDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            OpportunityDto target = BeanHelper.copyAs(e, OpportunityDto.class);
            target.setDataId(dataId);
            OpportunityDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setDealerServiceDEncrypt(dto.getDealerServiceD());
            target.setDealerServiceEncrypt(dto.getDealerService());
            target.setDealerSalesStaffNameDEncrypt(dto.getDealerSalesStaffNameD());
            target.setDealerSalesStaffNameEncrypt(dto.getDealerSalesStaffName());
            target.setExpectedDeliveryDateEncrypt(dto.getExpectedDeliveryDate());
            target.setSalesAccountCodeEncrypt(dto.getSalesAccountCode());
            target.setSpecialDeliveryAddressEncrypt(dto.getSpecialDeliveryAddress());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<OpportunityDto> updateList(List<OpportunityDto> opportunityList, String txId) {
        List<OpportunityDto> respList = new ArrayList<>();
        CacheList<OpportunityDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<OpportunityDto> dtoItemList = new ArrayList<>();
        opportunityList.forEach(e -> {
            Opportunity opportunity = Opportunity.dao.findById(e.getDataId());
            OpportunityDto cacheItem = BeanHelper.copyAs(e, OpportunityDto.class);
            dtoItemList.add(cacheItem);
            OpportunityDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,opportunity),OpportunityDto.class);
            OpportunityDto target = BeanHelper.copyAs(dto, OpportunityDto.class);
             dto = cryptoService.encryptModelColumns(dto);
            target.setDealerServiceDEncrypt(dto.getDealerServiceD());
            target.setDealerServiceEncrypt(dto.getDealerService());
            target.setDealerSalesStaffNameDEncrypt(dto.getDealerSalesStaffNameD());
            target.setDealerSalesStaffNameEncrypt(dto.getDealerSalesStaffName());
            target.setExpectedDeliveryDateEncrypt(dto.getExpectedDeliveryDate());
            target.setSalesAccountCodeEncrypt(dto.getSalesAccountCode());
            target.setSpecialDeliveryAddressEncrypt(dto.getSpecialDeliveryAddress());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        Opportunity opportunity = Opportunity.dao.findById(dataId);
        opportunity.setIsDelete(1);
        return opportunity.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        Opportunity opportunity = Opportunity.dao.findById(dataId);
        opportunity.setIsDelete(0);
        return opportunity.saveOrUpdate();
    }
    public List<OpportunityDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<Opportunity> quotesList = Opportunity.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, OpportunityDto.class)).collect(Collectors.toList());
    }
    public List<OpportunityDto> decryptUpdate(List<OpportunityDto> opportunityList) {
        List<OpportunityDto> respList =new ArrayList<>();
        String[] decryptColumn =new String[]{"expected_delivery_date","sales_account_code","special_delivery_address","dealer_service_d","dealer_service","dealer_sales_staff_name_d","dealer_sales_staff_name"};
        Db.tx(() -> {
            for (OpportunityDto e : opportunityList) {
                Opportunity opportunity =Opportunity.dao.findById(e.getDataId());
                if (opportunity == null) {
                    throw new BizException("无效的DataId");
                }
                Opportunity item=new Opportunity();
                BeanHelper.copyEncrypt(e, item);
                cryptoService.decryptoColumnModel(item,decryptColumn);
                BeanHelper.copy(item, opportunity);
                opportunity.setSfRecordId(e.getSfRecordId());
                if (!opportunity.saveOrUpdate()) {
                    throw new BizException(ResultCodeEnum.RT_ERROR);
                }
                respList.add(e);
            }
            return true;
        });
        return respList;
    }
    public List<OpportunityDto> decryptInsert(List<OpportunityDto> opportunityList) {
        List<OpportunityDto> respList =new ArrayList<>();
        Db.tx(() -> {
            for (OpportunityDto e : opportunityList) {
                Opportunity opportunity = new Opportunity();
                String dataId =idWorker.nextId();
                BeanHelper.copyEncrypt(e, opportunity);
                cryptoService.decryptoModel(opportunity);
                opportunity.setSfRecordId(e.getSfRecordId());
                opportunity.setDataId(Long.valueOf(dataId));
                if(!opportunity.save()) {
                    throw new BizException(ResultCodeEnum.RT_ERROR);
                }
                e.setDataId(dataId);
                respList.add(e);
            }
            return true;
        });
        return respList;
    }
}
src/main/java/com/deloitte/system/service/OrderService.java
New file
@@ -0,0 +1,218 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.enums.ResultCodeEnum;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.Opportunity;
import com.deloitte.system.model.Order;
import com.deloitte.system.request.OpportunityDto;
import com.deloitte.system.request.OrderDto;
import com.jfinal.plugin.activerecord.Db;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class OrderService {
    private String tableName = "order";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public OrderDto queryForOne(String dataId) {
        Order order = Order.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(order)) {
            return BeanHelper.copyAs(order,OrderDto.class);
        }
        return null;
    }
    public List<OrderDto> insertList(List<OrderDto> orderList, String txId) {
        List<OrderDto> respList = new ArrayList<>();
        CacheList<OrderDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<OrderDto> dtoItemList = new ArrayList<>();
        orderList.forEach(e -> {
            String dataId = idWorker.nextId();
            OrderDto cacheItem = BeanHelper.copyAs(e, OrderDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            OrderDto target = BeanHelper.copyAs(e, OrderDto.class);
            target.setDataId(dataId);
            OrderDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            cryptoService.convertor(dto,target);
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<OrderDto> updateList(List<OrderDto> orderList, String txId) {
        List<OrderDto> respList = new ArrayList<>();
        CacheList<OrderDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<OrderDto> dtoItemList = new ArrayList<>();
        orderList.forEach(e -> {
            Order order = Order.dao.findById(e.getDataId());
            OrderDto cacheItem = BeanHelper.copyAs(e, OrderDto.class);
            dtoItemList.add(cacheItem);
            OrderDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,order),OrderDto.class);
            OrderDto target = BeanHelper.copyAs(dto, OrderDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setSpecialDeliveryPhoneEncrypt(dto.getSpecialDeliveryPhone());
            target.setSpecialDeliveryPhoneDEncrypt(dto.getSpecialDeliveryPhoneD());
            target.setShippingRecieverEmailAdrEncrypt(dto.getShippingRecieverEmailAdr());
            target.setShipToContactIdEncrypt(dto.getShipToContactId());
            target.setCustomerAuthorizedByIdEncrypt(dto.getCustomerAuthorizedById());
            target.setBillToContactIdEncrypt(dto.getBillToContactId());
            target.setSpecialDeliveryContact2Encrypt(dto.getSpecialDeliveryContact2());
            target.setSpecialDeliveryContactTextEncrypt(dto.getSpecialDeliveryContact());
            target.setShippingAddressTextEncrypt(dto.getShippingAddressText());
            target.setSpecialDeliveryContact2DEncrypt(dto.getSpecialDeliveryContact2D());
            target.setEndUserEncrypt(dto.getEndUser());
            target.setEndUserDEncrypt(dto.getEndUserD());
            target.setSpecialDeliveryContactEncrypt(dto.getSpecialDeliveryContact());
            target.setSpecialDeliveryContactDEncrypt(dto.getSpecialDeliveryContactD());
            target.setPdfNNotifyPartyEncrypt(dto.getPdfNNotifyParty());
            target.setPdfNFaxEncrypt(dto.getPdfNFax());
            target.setPdfNContactEncrypt(dto.getPdfNContact());
            target.setPdfSTelEncrypt(dto.getPdfSTel());
            target.setPdfSPhoneEncrypt(dto.getPdfSPhone());
            target.setPdfSNameEncrypt(dto.getPdfSName());
            target.setPdfSFaxEncrypt(dto.getPdfSFax());
            target.setPdfFTelEncrypt(dto.getPdfFTel());
            target.setPdfFFaxEncrypt(dto.getPdfFFax());
            target.setPdfFContactPersonEncrypt(dto.getPdfFContactPerson());
            target.setPdfSAddsEncrypt(dto.getPdfSAdds());
            target.setPdfSAddressEncrypt(dto.getPdfSAddress());
            target.setPdfSellerEncrypt(dto.getPdfSeller());
            target.setPdfCTheconsigneEncrypt(dto.getPdfCTheconsigne());
            target.setPdfCTelEncrypt(dto.getPdfCTel());
            target.setPdfCFaxEncrypt(dto.getPdfCFax());
            target.setPdfCConsigneeEncrypt(dto.getPdfCConsignee());
            target.setPdfCContactEncrypt(dto.getPdfCContact());
            target.setPdfCAddrEncrypt(dto.getPdfCAddr());
            target.setSpecialDeliveryAddressEncrypt(dto.getSpecialDeliveryAddress());
            target.setSpecialDeliveryAddressDEncrypt(dto.getSpecialDeliveryAddressD());
            target.setDealerSalesStaffNameAEncrypt(dto.getDealerSalesStaffNameA());
            target.setPdfSignTitleEncrypt(dto.getPdfSignTitle());
            target.setPdfSignNameEncrypt(dto.getPdfSignName());
            target.setPdfSignaturePlacesEncrypt(dto.getPdfSignaturePlaces());
            target.setPdfByTelEncrypt(dto.getPdfByTel());
            target.setPdfByAddEncrypt(dto.getPdfByAdd());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        Order order = Order.dao.findById(dataId);
        order.setIsDelete(1);
        return order.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        Order order = Order.dao.findById(dataId);
        order.setIsDelete(0);
        return order.saveOrUpdate();
    }
    public List<OrderDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<Order> quotesList = Order.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, OrderDto.class)).collect(Collectors.toList());
    }
    public List<OrderDto> decryptUpdate(List<OrderDto> orderList) {
        List<OrderDto> respList =new ArrayList<>();
        String[] decryptColumn =new String[]{"expected_delivery_date","sales_account_code","special_delivery_address","dealer_service_d","dealer_service","dealer_sales_staff_name_d","dealer_sales_staff_name"};
        Db.tx(() -> {
            for (OrderDto e : orderList) {
                Order order =Order.dao.findById(e.getDataId());
                if (order == null) {
                    throw new BizException("无效的DataId");
                }
                Order item=new Order();
                BeanHelper.copyEncrypt(e, item);
                cryptoService.decryptoColumnModel(item,decryptColumn);
                BeanHelper.copyEncrypt(item, order);
                order.setSfRecordId(e.getSfRecordId());
                if (!order.saveOrUpdate()) {
                    throw new BizException(ResultCodeEnum.RT_ERROR);
                }
                respList.add(e);
            }
            return true;
        });
        return respList;
    }
    public List<OrderDto> decryptInsert(List<OrderDto> orderList) {
        List<OrderDto> respList =new ArrayList<>();
        Db.tx(() -> {
            for (OrderDto e : orderList) {
                Order order = new Order();
                String dataId =idWorker.nextId();
                BeanHelper.copyEncrypt(e, order);
                cryptoService.decryptoModel(order);
                order.setSfRecordId(e.getSfRecordId());
                order.setDataId(Long.valueOf(dataId));
                if(!order.save()) {
                    throw new BizException(ResultCodeEnum.RT_ERROR);
                }
                e.setDataId(dataId);
                respList.add(e);
            }
            return true;
        });
        return respList;
    }
}
src/main/java/com/deloitte/system/service/QuotesService.java
New file
@@ -0,0 +1,143 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.Quotes;
import com.deloitte.system.request.QuotesDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class QuotesService {
    private  String tableName="quotes";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public QuotesDto queryForOne(String dataId) {
         Quotes quotes =Quotes.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(quotes)){
            QuotesDto dto = BeanHelper.copyAs(quotes, QuotesDto.class);
            return dto;
        }
        return null;
    }
    public List<QuotesDto> insertList(List<QuotesDto> quotesDtoList, String txId) {
        List<QuotesDto> respList = new ArrayList<>();
        CacheList<QuotesDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<QuotesDto> dtoItemList = new ArrayList<>();
        quotesDtoList.forEach(e -> {
            String dataId = idWorker.nextId();
            QuotesDto cacheItem = BeanHelper.copyAs(e, QuotesDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            QuotesDto target = BeanHelper.copyAs(e, QuotesDto.class);
            target.setDataId(dataId);
            QuotesDto dto =  cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setAuthorEncrypt(dto.getAuthor());
            target.setMessageEncrypt(dto.getMessage());
            target.setContactEmailEncrypt(dto.getContactEmail());
            target.setContactFaxEncrypt(dto.getContactFax());
            target.setContactNameEncrypt(dto.getContactName());
            target.setContactPhoneEncrypt(dto.getContactPhone());
            target.setPrimaryRecipientEncrypt(dto.getPrimaryRecipient());
            target.setShipToEncrypt(dto.getShipTo());
            target.setBillToEncrypt(dto.getBillTo());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<QuotesDto> updateList(List<QuotesDto> quotesDtoList, String txId) {
        List<QuotesDto> respList = new ArrayList<>();
        CacheList<QuotesDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<QuotesDto> dtoItemList = new ArrayList<>();
        quotesDtoList.forEach(e -> {
            Quotes quotes = Quotes.dao.findById(e.getDataId());
            QuotesDto cacheItem = BeanHelper.copyAs(e, QuotesDto.class);
            dtoItemList.add(cacheItem);
            QuotesDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,quotes),QuotesDto.class);
            QuotesDto target = BeanHelper.copyAs(dto, QuotesDto.class);
            cryptoService.encryptModelColumns(dto);
            target.setAuthorEncrypt(dto.getAuthor());
            target.setMessageEncrypt(dto.getMessage());
            target.setContactEmailEncrypt(dto.getContactEmail());
            target.setContactFaxEncrypt(dto.getContactFax());
            target.setContactNameEncrypt(dto.getContactName());
            target.setContactPhoneEncrypt(dto.getContactPhone());
            target.setPrimaryRecipientEncrypt(dto.getPrimaryRecipient());
            target.setShipToEncrypt(dto.getShipTo());
            target.setBillToEncrypt(dto.getBillTo());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        Quotes quotes = Quotes.dao.findById(dataId);
        quotes.setIsDelete(1);
        return quotes.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        Quotes quotes = Quotes.dao.findById(dataId);
        quotes.setIsDelete(0);
        return quotes.saveOrUpdate();
    }
    public List<QuotesDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<Quotes> quotesList = Quotes.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, QuotesDto.class)).collect(Collectors.toList());
    }
}
src/main/java/com/deloitte/system/service/RepairService.java
New file
@@ -0,0 +1,132 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.*;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.Order;
import com.deloitte.system.model.Repair;
import com.deloitte.system.request.OrderDto;
import com.deloitte.system.request.RepairDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class RepairService {
    private String tableName = "repair";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public RepairDto queryForOne(String dataId) {
        Repair repair = Repair.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(repair)) {
            return BeanHelper.copyAs(repair,RepairDto.class);
        }
        return null;
    }
    public List<RepairDto> insertList(List<RepairDto> repairList, String txId) {
        List<RepairDto> respList = new ArrayList<>();
        CacheList<RepairDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<RepairDto> dtoItemList = new ArrayList<>();
        repairList.forEach(e -> {
            String dataId = idWorker.nextId();
            RepairDto cacheItem = BeanHelper.copyAs(e, RepairDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            RepairDto target = BeanHelper.copyAs(e, RepairDto.class);
            target.setDataId(dataId);
            RepairDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setTelephonenEncrypt(dto.getTelephonen());
            target.setContactDEncrypt(dto.getContactD());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<RepairDto> updateList(List<RepairDto> repairList, String txId) {
        List<RepairDto> respList = new ArrayList<>();
        CacheList<RepairDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<RepairDto> dtoItemList = new ArrayList<>();
        repairList.forEach(e -> {
            Repair repair = Repair.dao.findById(e.getDataId());
            RepairDto cacheItem = BeanHelper.copyAs(e, RepairDto.class);
            dtoItemList.add(cacheItem);
            RepairDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,repair),RepairDto.class);
            RepairDto target = BeanHelper.copyAs(dto, RepairDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setTelephonenEncrypt(dto.getTelephonen());
            target.setContactDEncrypt(dto.getContactD());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag) {
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        Repair repair = Repair.dao.findById(dataId);
        repair.setIsDelete(1);
        return repair.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        Repair repair = Repair.dao.findById(dataId);
        repair.setIsDelete(0);
        return repair.saveOrUpdate();
    }
    public List<RepairDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<Repair> quotesList = Repair.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, RepairDto.class)).collect(Collectors.toList());
    }
}
src/main/java/com/deloitte/system/service/SwoService.java
New file
@@ -0,0 +1,150 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.Swo;
import com.deloitte.system.request.SwoDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class SwoService {
    private String tableName="swo";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public SwoDto queryForOne(String dataId) {
        Swo swo =Swo.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(swo)){
            SwoDto dto = BeanHelper.copyAs(swo, SwoDto.class);
            return dto;
        }
        return null;
    }
    public List<SwoDto> insertList(List<SwoDto> swoDtoList, String txId) {
        List<SwoDto> respList = new ArrayList<>();
        CacheList<SwoDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<SwoDto> dtoItemList = new ArrayList<>();
        swoDtoList.forEach(e -> {
            String dataId = idWorker.nextId();
            SwoDto cacheItem = BeanHelper.copyAs(e, SwoDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            SwoDto target = BeanHelper.copyAs(e, SwoDto.class);
            target.setDataId(dataId);
            SwoDto dto =  cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setAuthorEncrypt(dto.getAuthor());
            target.setBccEncrypt(dto.getBcc());
            target.setBccNameEncrypt(dto.getBccName());
            target.setCcEncrypt(dto.getCc());
            target.setCcNameEncrypt(dto.getCcName());
            target.setContactEncrypt(dto.getContact());
            target.setContactNameHiddenEncrypt(dto.getContactNameHidden());
            target.setEmailEncrypt(dto.getEmail());
            target.setMessageEncrypt(dto.getMessage());
            target.setPrimaryRecipientEncrypt(dto.getPrimaryRecipient());
            target.setRecipientEncrypt(dto.getRecipient());
            target.setSendEncrypt(dto.getSend());
            target.setToNameEncrypt(dto.getToName());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<SwoDto> updateList(List<SwoDto> swoDtoList, String txId) {
        List<SwoDto> respList = new ArrayList<>();
        CacheList<SwoDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<SwoDto> dtoItemList = new ArrayList<>();
        swoDtoList.forEach(e -> {
            Swo swo = Swo.dao.findById(e.getDataId());
            SwoDto cacheItem = BeanHelper.copyAs(e, SwoDto.class);
            dtoItemList.add(cacheItem);
            SwoDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,swo),SwoDto.class);
            SwoDto target = BeanHelper.copyAs(dto, SwoDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setAuthorEncrypt(dto.getAuthor());
            target.setBccEncrypt(dto.getBcc());
            target.setBccNameEncrypt(dto.getBccName());
            target.setCcEncrypt(dto.getCc());
            target.setCcNameEncrypt(dto.getCcName());
            target.setContactEncrypt(dto.getContact());
            target.setContactNameHiddenEncrypt(dto.getContactNameHidden());
            target.setEmailEncrypt(dto.getEmail());
            target.setMessageEncrypt(dto.getMessage());
            target.setPrimaryRecipientEncrypt(dto.getPrimaryRecipient());
            target.setRecipientEncrypt(dto.getRecipient());
            target.setSendEncrypt(dto.getSend());
            target.setToNameEncrypt(dto.getToName());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        Swo swo = Swo.dao.findById(dataId);
        swo.setIsDelete(1);
        return swo.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        Swo swo = Swo.dao.findById(dataId);
        swo.setIsDelete(1);
        return swo.saveOrUpdate();
    }
    public List<SwoDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<Swo> quotesList = Swo.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, SwoDto.class)).collect(Collectors.toList());
    }
}
src/main/java/com/deloitte/system/service/TSRepairService.java
New file
@@ -0,0 +1,130 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.TSRepair;
import com.deloitte.system.request.TSRepairDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class TSRepairService {
    private  String tableName = "ts_repair";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public TSRepairDto queryForOne(String dataId) {
        TSRepair tsRepair =TSRepair.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(tsRepair)){
            TSRepairDto dto = BeanHelper.copyAs(tsRepair, TSRepairDto.class);
            return dto;
        }
        return null;
    }
    public List<TSRepairDto> insertList(List<TSRepairDto> tsRepairDtoList, String txId) {
        List<TSRepairDto> respList = new ArrayList<>();
        CacheList<TSRepairDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<TSRepairDto> dtoItemList = new ArrayList<>();
        tsRepairDtoList.forEach(e -> {
            String dataId = idWorker.nextId();
            TSRepairDto cacheItem = BeanHelper.copyAs(e, TSRepairDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            TSRepairDto target = BeanHelper.copyAs(e, TSRepairDto.class);
            target.setDataId(dataId);
            TSRepairDto dto =  cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setBusinessAContactEncrypt(dto.getBusinessAContact());
            target.setBusinessAPhoneEncrypt(dto.getBusinessAPhone());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<TSRepairDto> updateList(List<TSRepairDto> tsRepairDtoList, String txId) {
        List<TSRepairDto> respList = new ArrayList<>();
        CacheList<TSRepairDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<TSRepairDto> dtoItemList = new ArrayList<>();
        tsRepairDtoList.forEach(e -> {
            TSRepair tsRepair = TSRepair.dao.findById(e.getDataId());
            TSRepairDto cacheItem = BeanHelper.copyAs(e, TSRepairDto.class);
            dtoItemList.add(cacheItem);
            TSRepairDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,tsRepair),TSRepairDto.class);
            TSRepairDto target = BeanHelper.copyAs(dto, TSRepairDto.class);
             dto  = cryptoService.encryptModelColumns(dto);
            target.setBusinessAPhoneEncrypt(dto.getBusinessAPhone());
            target.setBusinessAContactEncrypt(dto.getBusinessAContact());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        TSRepair tsRepair = TSRepair.dao.findById(dataId);
        tsRepair.setIsDelete(1);
        return tsRepair.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        TSRepair tsRepair = TSRepair.dao.findById(dataId);
        tsRepair.setIsDelete(0);
        return tsRepair.saveOrUpdate();
    }
    public List<TSRepairDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<TSRepair> quotesList = TSRepair.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, TSRepairDto.class)).collect(Collectors.toList());
    }
}
src/main/java/com/deloitte/system/service/UserFaultInfoService.java
New file
@@ -0,0 +1,132 @@
package com.deloitte.system.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.common.core.constant.GlobalConst;
import com.common.core.exception.BizException;
import com.common.core.service.CryptoService;
import com.common.core.utils.BeanHelper;
import com.common.core.utils.DesensitiveUtils;
import com.common.core.utils.IdUtils;
import com.common.redis.util.RedisUtil;
import com.deloitte.system.model.CacheList;
import com.deloitte.system.model.UserFaultInfo;
import com.deloitte.system.request.UserFaultInfoDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_INSERT;
import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_UPDATE;
@Service
public class UserFaultInfoService {
    private String tableName="user_fault_info";
    @Autowired
    private CryptoService cryptoService;
    @Autowired
    private IdUtils idWorker;
    @Autowired
    private RedisUtil redisUtil;
    public UserFaultInfoDto queryForOne(String dataId) {
        UserFaultInfo userFaultInfo =UserFaultInfo.dao.findById(dataId);
        if (!ObjectUtil.isEmpty(userFaultInfo)){
            UserFaultInfoDto dto = BeanHelper.copyAs(userFaultInfo, UserFaultInfoDto.class);
            return dto;
        }
        return null;
    }
    public List<UserFaultInfoDto> insertList(List<UserFaultInfoDto> userFaultInfoDtoList, String txId) {
        List<UserFaultInfoDto> respList = new ArrayList<>();
        CacheList<UserFaultInfoDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<UserFaultInfoDto> dtoItemList = new ArrayList<>();
        userFaultInfoDtoList.forEach(e -> {
            String dataId = idWorker.nextId();
            UserFaultInfoDto cacheItem = BeanHelper.copyAs(e, UserFaultInfoDto.class);
            cacheItem.setDataId(dataId);
            dtoItemList.add(cacheItem);
            UserFaultInfoDto target = BeanHelper.copyAs(e, UserFaultInfoDto.class);
            target.setDataId(dataId);
            UserFaultInfoDto dto = cryptoService.encryptModelColumns(e);
            dto.setDataId(dataId);
            target.setUfContactEncrypt(dto.getUfContact());
            target.setUfPhoneEncrypt(dto.getUfPhone());
            target.setInboundEmailAddressEncrypt(dto.getInboundEmailAddress());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String insertJsonString = JSONArray.toJSONString(dtoCacheList);
        String insertKey = PIPL_UNCONFIRM_INSERT + txId;
        boolean redisFlag = redisUtil.set(insertKey, insertJsonString, GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public List<UserFaultInfoDto> updateList(List<UserFaultInfoDto> userFaultInfoDtoList, String txId) {
        List<UserFaultInfoDto> respList = new ArrayList<>();
        CacheList<UserFaultInfoDto> dtoCacheList = new CacheList<>();
        dtoCacheList.setTableName(tableName);
        List<UserFaultInfoDto> dtoItemList = new ArrayList<>();
        userFaultInfoDtoList.forEach(e -> {
            UserFaultInfo userFaultInfo = UserFaultInfo.dao.findById(e.getDataId());
            UserFaultInfoDto cacheItem = BeanHelper.copyAs(e, UserFaultInfoDto.class);
            dtoItemList.add(cacheItem);
            UserFaultInfoDto dto = BeanHelper.copyAs(BeanHelper.copy(cacheItem,userFaultInfo),UserFaultInfoDto.class);
            UserFaultInfoDto target = BeanHelper.copyAs(dto, UserFaultInfoDto.class);
            dto = cryptoService.encryptModelColumns(dto);
            target.setUfContactEncrypt(dto.getUfContact());
            target.setUfPhoneEncrypt(dto.getUfPhone());
            target.setInboundEmailAddressEncrypt(dto.getInboundEmailAddress());
            DesensitiveUtils.format(target);
            respList.add(target);
        });
        dtoCacheList.setData(dtoItemList);
        String updateJsonString = JSONArray.toJSONString(dtoCacheList);
        String updateKey = PIPL_UNCONFIRM_UPDATE + txId;
        boolean redisFlag = redisUtil.set(updateKey, updateJsonString,GlobalConst.DATA_EXPIRE);
        if (!redisFlag){
            throw new BizException("缓存写入失败");
        }
        return respList;
    }
    public Boolean deleteOne(String dataId) {
        UserFaultInfo userFaultInfo = UserFaultInfo.dao.findById(dataId);
        userFaultInfo.setIsDelete(1);
        return userFaultInfo.saveOrUpdate();
    }
    public Boolean undeleteOne(String dataId) {
        UserFaultInfo userFaultInfo = UserFaultInfo.dao.findById(dataId);
        userFaultInfo.setIsDelete(0);
        return userFaultInfo.saveOrUpdate();
    }
    public List<UserFaultInfoDto> search(List<String> idList){
        if (CollUtil.isEmpty(idList)) {
            return null;
        }
        StringBuilder queryParam = new StringBuilder();
        queryParam.append("and id in ").append(idList.stream().collect(Collectors.joining(", ", "(", ")")));
        List<UserFaultInfo> quotesList = UserFaultInfo.dao.findList(queryParam.toString());
        return quotesList.stream()
                .map(quotes -> BeanHelper.copyAs(quotes, UserFaultInfoDto.class)).collect(Collectors.toList());
    }
}
src/main/resources/dev/application.yml
New file
@@ -0,0 +1 @@
server:   port: 8889   servlet:     context-path: /ssbgapi #swagger禁用 swagger:   production: true spring:   mvc:     pathmatch:       matching-strategy: ant_path_matcher   redis:     host: sfpi-redis-ssbg.q82q8k.ng.0001.cnw1.cache.amazonaws.com.cn     port: 6379     database: 0     password: mail:   smtp:     host: smtp.qiye.aliyun.com     port: 465     protocol: smtp   imap:     host: imap.qiye.aliyun.com     port: 993     protocol: imap   default-encoding: utf-8   properties:     mail:       smtp:         auth: true jfinal:   dialect: com.jfinal.plugin.activerecord.dialect.MysqlDialect   sql-templates:     - classpath:sqls/*.sql   show-sql: true custom:   datacenter-id: 0   worker-id: 0 aws:   region: cn-northwest-1   secrets:     mysql: test/mysqlsecretssbg     sm4: test/SM4Secret     systemauth: test/systemauthssbg     mailauth: test/mailauthssbg   s3:     bucketName: ssbgbucketdev salesforce:   tokenUrl: https://ssbg--pipl.my.salesforce.com/services/oauth2/token   baseUrl: https://ssbg--pipl.my.salesforce.com/   sbgurl: https://ssbg--pipl.my.salesforce.com/services/apexrest/   referer: https://ssbg--pipl.my.salesforce.com/,https://ssbg--pipl--c.visualforce.com/ sap:   url: https://wdp.olympus.com.cn:44301/RESTAdapter/ # s3文件查询地址 file:   url: https://sfpi-ssbg-test.olympuschina.com:8080/ssbgapi/file/
src/main/resources/dev/logback.xml
New file
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/home/ec2-user/deploy/dev/ssbglog"/>
    <!-- 定义日志格式  -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-info.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-error.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- 日志输出级别 -->
    <logger name="org.springframework" level="info"/>
    <logger name="com.deloitte" level="info"/>
    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
    </root>
</configuration>
src/main/resources/local/application.yml
New file
@@ -0,0 +1 @@
server:   port: 8889   servlet:     context-path: /ssbgapi #swagger启用 swagger:   production: false spring:   profiles:     active: local   mvc:     pathmatch:       matching-strategy: ant_path_matcher   redis:     host: localhost     port: 6379     database: 7     password: mail:   smtp:     host: smtp.qiye.aliyun.com     port: 465     protocol: smtp   imap:     host: imap.qiye.aliyun.com     port: 993     protocol: imap   default-encoding: utf-8   properties:     mail:       smtp:         auth: true jfinal:   dialect: com.jfinal.plugin.activerecord.dialect.MysqlDialect   sql-templates:     - classpath:sqls/*.sql   show-sql: true custom:   datacenter-id: 0   worker-id: 0 aws:   region: cn-northwest-1   secrets:     mysql: test/mysqlsecretssbg     sm4: test/SM4Secret     systemauth: test/systemauthssbg     mailauth: test/mailauthssbg   s3:     bucketName: ssbgbucketdev salesforce:   tokenUrl: https://ssbg--pipl.my.salesforce.com/services/oauth2/token   baseUrl: https://ssbg--pipl.my.salesforce.com/   sbgurl: https://ssbg--pipl.my.salesforce.com/services/apexrest/   referer: https://ssbg--pipl.my.salesforce.com/,https://ssbg--pipl--c.visualforce.com/ sap:   url: https://wdp.olympus.com.cn:44301/RESTAdapter/ # s3文件查询地址 file:   url: https://sfpi-mebg-test.olympuschina.com:8080/ssbgapi/file/
src/main/resources/local/logback.xml
New file
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/home/ec2-user/ssbglog"/>
    <!-- 定义日志格式  -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-info.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-error.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- 日志输出级别 -->
    <logger name="org.springframework" level="info"/>
    <logger name="com.deloitte" level="info"/>
    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
    </root>
</configuration>
src/main/resources/prod1/application.yml
New file
@@ -0,0 +1 @@
server:   port: 8891   servlet:     context-path: /ssbgapi #swagger禁用 swagger:   production: true spring:   mvc:     pathmatch:       matching-strategy: ant_path_matcher   redis:     host: sfpi-redis-ssbg.q82q8k.ng.0001.cnw1.cache.amazonaws.com.cn     port: 6379     database: 2     password: mail:   smtp:     host: smtp.qiye.aliyun.com     port: 465     protocol: smtp   imap:     host: imap.qiye.aliyun.com     port: 993     protocol: imap   default-encoding: utf-8   properties:     mail:       smtp:         auth: true jfinal:   dialect: com.jfinal.plugin.activerecord.dialect.MysqlDialect   sql-templates:     - classpath:sqls/*.sql   show-sql: true custom:   datacenter-id: 0   worker-id: 0 aws:   region: cn-northwest-1   secrets:     mysql: prod/mysqlsecretssbg     sm4: prod/SM4Secret     systemauth: prod/systemauthssbg     mailauth: prod/mailauthssbg   s3:     bucketName: ssbgbucketpro salesforce:   tokenUrl: https://ap6.salesforce.com/services/oauth2/token   baseUrl: https://ssbg.my.salesforce.com/   sbgurl: https://ssbg.my.salesforce.com/services/apexrest/   referer: https://ssbg.my.salesforce.com/,https://ssbg--c.ap6.visual.force.com/,https://ssbgdealer.force.com/,https://ssbg--c.ap26.visual.force.com/ sap:   url: https://wdp.olympus.com.cn:44302/RESTAdapter/ # s3文件查询地址 file:   url: https://sfpi-prod.evidentscientific.com.cn:8082/ssbgapi/file/
src/main/resources/prod1/logback.xml
New file
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/home/ec2-user/deploy/prod/ssbglog"/>
    <!-- 定义日志格式  -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-info.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-error.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- 日志输出级别 -->
    <logger name="org.springframework" level="info"/>
    <logger name="com.deloitte" level="info"/>
    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
    </root>
</configuration>
src/main/resources/prod2/application.yml
New file
@@ -0,0 +1 @@
server:   port: 8891   servlet:     context-path: /ssbgapi #swagger禁用 swagger:   production: true spring:   mvc:     pathmatch:       matching-strategy: ant_path_matcher   redis:     host: sfpi-redis-ssbg.q82q8k.ng.0001.cnw1.cache.amazonaws.com.cn     port: 6379     database: 2     password: mail:   smtp:     host: smtp.qiye.aliyun.com     port: 465     protocol: smtp   imap:     host: imap.qiye.aliyun.com     port: 993     protocol: imap   default-encoding: utf-8   properties:     mail:       smtp:         auth: true jfinal:   dialect: com.jfinal.plugin.activerecord.dialect.MysqlDialect   sql-templates:     - classpath:sqls/*.sql   show-sql: true custom:   datacenter-id: 0   worker-id: 1 aws:   region: cn-northwest-1   secrets:     mysql: prod/mysqlsecretssbg     sm4: prod/SM4Secret     systemauth: prod/systemauthssbg     mailauth: prod/mailauthssbg   s3:     bucketName: ssbgbucketpro salesforce:   tokenUrl: https://ap6.salesforce.com/services/oauth2/token   baseUrl: https://ssbg.my.salesforce.com/   sbgurl: https://ssbg.my.salesforce.com/services/apexrest/   referer: https://ssbg.my.salesforce.com/,https://ssbg--c.ap6.visual.force.com/,https://ssbgdealer.force.com/,https://ssbg--c.ap26.visual.force.com/ sap:   url: https://wdp.olympus.com.cn:44302/RESTAdapter/ # s3文件查询地址 file:   url: https://sfpi-prod.evidentscientific.com.cn:8082/ssbgapi/file/
src/main/resources/prod2/logback.xml
New file
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/home/ec2-user/deploy/prod/ssbglog"/>
    <!-- 定义日志格式  -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-info.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-error.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- 日志输出级别 -->
    <logger name="org.springframework" level="info"/>
    <logger name="com.deloitte" level="info"/>
    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
    </root>
</configuration>
src/main/resources/sqls/theme.sql
New file
@@ -0,0 +1,5 @@
#namespace("user")
  #sql("queryUser")
    select * from user t where t.User like concat('%',#para(title),'%')
  #end
#end
src/main/resources/stg/application.yml
New file
@@ -0,0 +1 @@
server:   port: 8890   servlet:     context-path: /ssbgapi #swagger禁用 swagger:   production: true spring:   mvc:     pathmatch:       matching-strategy: ant_path_matcher   redis:     host: sfpi-redis-ssbg.q82q8k.ng.0001.cnw1.cache.amazonaws.com.cn     port: 6379     database: 1     password: mail:   smtp:     host: smtp.qiye.aliyun.com     port: 465     protocol: smtp   imap:     host: imap.qiye.aliyun.com     port: 993     protocol: imap   default-encoding: utf-8   properties:     mail:       smtp:         auth: true jfinal:   dialect: com.jfinal.plugin.activerecord.dialect.MysqlDialect   sql-templates:     - classpath:sqls/*.sql   show-sql: true custom:   datacenter-id: 0   worker-id: 0 aws:   region: cn-northwest-1   secrets:     mysql: stg/mysqlsecretssbg     sm4: stg/SM4Secret     systemauth: stg/systemauthssbg     mailauth: test/mailauthssbg   s3:     bucketName: ssbgbucketstg salesforce:   tokenUrl: https://ssbg--ssbgtest.my.salesforce.com/services/oauth2/token   baseUrl: https://ssbg--ssbgtest.my.salesforce.com/   sbgurl: https://ssbg--ssbgtest.my.salesforce.com/services/apexrest/   referer: https://ssbg--ssbgtest.my.salesforce.com/,https://ssbg--ssbgtest--c.visualforce.com/,https://ssbgtest-ssbgdealer.cs112.force.com/ sap:   url: http://wdp.olympus.com.cn:8089/RESTAdapter/ # s3文件查询地址 file:   url: https://sfpi-test.Evidentscientific.com.cn:8081/ssbgapi/file/
src/main/resources/stg/logback.xml
New file
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/home/ec2-user/deploy/stg/ssbglog"/>
    <!-- 定义日志格式  -->
    <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] [%thread] [%-30.30logger{30}] %msg%n"/>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-info.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${LOG_HOME}/sys-error.%d{yyyy-MM-dd}.zip</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>ERROR</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:拒绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- 日志输出级别 -->
    <logger name="org.springframework" level="info"/>
    <logger name="com.deloitte" level="info"/>
    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="file_info"/>
        <appender-ref ref="file_error"/>
    </root>
</configuration>