db7cc809acf1ffdd006c0d5de5dea4b65bb297ff..5d7b65670282a4fad015e37d567cfa171b162052
2 天以前 huliguo
基础代码
5d7b65 对比 | 目录
539个文件已添加
47208 ■■■■■ 已修改文件
.idea/compiler.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/dataSources.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/encodings.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/jarRepositories.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/misc.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/vcs.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/pom.xml 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/PaoTuiApplication.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/PaoTuiServletInitializer.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/AddressBookController.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/AgreementController.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/AppUserController.java 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/BannerController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityController.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityCourierController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/CourierController.java 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/EvaluationController.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/FeedbackController.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/OrderController.java 299 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/PhoneController.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/RegionController.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/ReportController.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/SystemConfigController.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/UserCancellationLogController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipOrderController.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipSettingController.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java 314 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/controller/task/MessageTask.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/META-INF/spring-devtools.properties 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/application-druid.yml 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/application.yml 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/banner.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/i18n/messages.properties 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/logback.xml 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/mybatis/mybatis-config.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/target/classes/META-INF/spring-devtools.properties 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/target/classes/application-druid.yml 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/target/classes/application.yml 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/target/classes/banner.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/target/classes/i18n/messages.properties 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/target/classes/logback.xml 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/target/classes/mybatis/mybatis-config.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/pom.xml 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/DataScope.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/DataSource.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/Excel.java 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/Excels.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/Log.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/Constants.java 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/GenConstants.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/SecurityConstants.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/TokenConstants.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/constant/UserConstants.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/BaseResult.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/R.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/ResponseUtils.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java 274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/text/Convert.java 1012 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/easyExcel/BigDecimalConverter.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/easyExcel/DateConverter.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/easyExcel/MultiDropdownWriteHandler.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/easyExcel/NumberConverter.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/easyExcel/StringConverter.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/BusinessType.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/LimitType.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/OperatorType.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/enums/UserStatus.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/BaseException.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/GlobalException.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/ServiceException.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/UtilException.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/file/FileException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/user/UserException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/filter/XssFilter.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/Arith.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/DateUtils.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/DictUtils.java 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/LogUtils.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/PageUtils.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/StringUtils.java 710 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/Threads.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java 367 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java 570 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java 382 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java 1900 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java 410 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java 484 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/xss/Xss.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/src/main/java/com/ruoyi/common/xss/XssValidator.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/target/maven-archiver/pom.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/pom.xml 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/AppUserStatusConstant.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/DelFlagConstant.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/IsDefaultConstant.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstLoginConstant.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstOrder.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/JwtClaimsConstant.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/MessageConstant.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/StatusConstant.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/constant/SystemConfigTypeConstant.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/context/BaseContext.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/AddressBook.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Agreement.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/AppUser.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Banner.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Community.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/CommunityCourier.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Feedback.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Order.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Phone.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Region.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Report.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/SystemConfig.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/UserCancellationLog.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/VipOrder.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/VipSetting.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/interceptor/APPJwtTokenInterceptor.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/AddressBookMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/AgreementMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/AppUserMapper.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/BannerMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityCourierMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityMapper.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/CourierMapper.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/EvaluationMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/FeedbackMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/OrderMapper.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/PhoneMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/RegionMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/ReportMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/SystemConfigMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/UserCancellationLogMapper.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/VipOrderMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/mapper/VipSettingMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddAddressBookDTO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddReportDTO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AgreementDTO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AppletLogin.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/BirthDayDTO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/ConfirmOrderDTO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/MobileLoginDTO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderPayment.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsDTO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsVO.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/RegisterDTO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/SetConfirmOrderDTO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/StartPageSetDto.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/UpdateAddressBookDTO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/VipPaymentDTO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddBannerDTO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCommunityDTO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AppUserPageListDTO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CommunityPageListDTO.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CourierPageListDTO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCommunityDTO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCourierDTO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FeedbackPageListDTO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FinanceStatisticsDTO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/OrderPageListDTO.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/ReportPageListDTO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/SetPriceDTO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/UserStatsDTO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookByCommunityIdVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookListVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserInfoVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserOrderListVO.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/BannerVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CommunityListVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CompleteOrderDTO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/ConfirmOrderVO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierInfoVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierOrderListVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierStatisticsVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderDetailVO.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderPageVO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderTopInfoVO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/UserTopInfoVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/VipInfoListVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/login/LoginVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCommunityListVO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCourierListVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserPageListVO.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserSysDetailVO.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerDetailVo.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerPageListVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunityPageListVO.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunitySysDetailVO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierPageListVO.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/EditBannerDTO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FeedbackPageListVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderSysDetailVO.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/RefundVipOrderVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/ReportPageListVO.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/UserStatsVO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/AddressBookService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/AgreementService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/AppUserService.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/BannerService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/CommunityCourierService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/CommunityService.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/CourierService.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/EvaluationService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/FeedbackService.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/OrderService.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/PhoneService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/RegionService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/ReportService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/SystemConfigService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/UserCancellationLogService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/VipOrderService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/VipSettingService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/AddressBookServiceImpl.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/AgreementServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java 557 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/BannerServiceImpl.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityCourierServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityServiceImpl.java 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/CourierServiceImpl.java 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/FeedbackServiceImpl.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java 607 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/PhoneServiceImpl.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/RegionServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/ReportServiceImpl.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/SystemConfigServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/UserCancellationLogServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipOrderServiceImpl.java 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipSettingServiceImpl.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/AES.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/BatchNumberUtils.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/CloseOrderResult.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/FrpCodeEnum.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/JwtUtil.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/MD5AndKL.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentCycleHelper.java 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentUtil.java 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/QueryOrderResult.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/QueryRefundResult.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/RedisService.java 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/RefundCallbackResult.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/RefundResult.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/SMSUtil.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/TaskUtil.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/TokenBlacklistService.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayCallbackResult.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayResult.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/WXCore.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppAuthenticationToken.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppSecurityProperties.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/WeChatUtil.java 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/WebSocketConfig.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/WxPKCS7Encoder.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/AddressBookMapper.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/AgreementMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/AppUserMapper.xml 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/BannerMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/CommunityCourierMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/CommunityMapper.xml 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/CourierMapper.xml 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/EvaluationMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/FeedbackMapper.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/OrderMapper.xml 426 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/PhoneMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/RegionMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/ReportMapper.xml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/SystemConfigMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/UserCancellationLogMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/VipOrderMapper.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/VipSettingMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/templates/bjd.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/AddressBookMapper.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/AgreementMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/AppUserMapper.xml 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/BannerMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/CommunityCourierMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/CommunityMapper.xml 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/CourierMapper.xml 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/EvaluationMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/FeedbackMapper.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/OrderMapper.xml 426 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/PhoneMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/RegionMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/ReportMapper.xml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/SystemConfigMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/UserCancellationLogMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/VipOrderMapper.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/mapper/VipSettingMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/classes/templates/bjd.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/maven-archiver/pom.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/pom.xml 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/target/maven-archiver/pom.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/pom.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysCache.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysConfig.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysNotice.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysPost.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysRoleDTO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysUserDTO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/dto/EditSysRoleDTO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/dto/SetPasswordDTO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/vo/EditSysUserDTO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/vo/MenuTreeVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/vo/SysDeptPageVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/vo/SysRolePageVO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/vo/SysRoleVO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserPageListVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysPostService.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 369 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 598 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 498 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 682 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysConfigMapper.xml 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysDeptMapper.xml 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysDictDataMapper.xml 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysDictTypeMapper.xml 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysLogininforMapper.xml 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysMenuMapper.xml 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysNoticeMapper.xml 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysOperLogMapper.xml 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysPostMapper.xml 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysRoleMapper.xml 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysUserMapper.xml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysUserPostMapper.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/src/main/resources/mapper/system/SysUserRoleMapper.xml 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysConfigMapper.xml 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysDeptMapper.xml 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysDictDataMapper.xml 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysDictTypeMapper.xml 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysLogininforMapper.xml 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysMenuMapper.xml 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysNoticeMapper.xml 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysOperLogMapper.xml 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysPostMapper.xml 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysRoleDeptMapper.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysRoleMapper.xml 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysRoleMenuMapper.xml 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysUserMapper.xml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysUserPostMapper.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/classes/mapper/system/SysUserRoleMapper.xml 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/maven-archiver/pom.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/compiler.xml
New file
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="CompilerConfiguration">
    <annotationProcessing>
      <profile name="Maven default annotation processors profile" enabled="true">
        <sourceOutputDir name="target/generated-sources/annotations" />
        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
        <outputRelativeToContentRoot value="true" />
        <module name="pt-admin" />
        <module name="pt-common" />
        <module name="cz-admin" />
        <module name="pt-framework" />
        <module name="pt-system" />
        <module name="cz-common" />
        <module name="cz-bussiness" />
        <module name="pt-errand" />
        <module name="cz-framework" />
        <module name="cz-system" />
      </profile>
    </annotationProcessing>
    <bytecodeTargetLevel>
      <module name="pt-bussiness" target="1.8" />
    </bytecodeTargetLevel>
  </component>
</project>
.idea/dataSources.xml
New file
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
    <data-source source="LOCAL" name="paotui@localhost" uuid="bcd09324-0031-43ab-b1d0-35e461c7af72">
      <driver-ref>mysql.8</driver-ref>
      <synchronize>true</synchronize>
      <jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
      <jdbc-url>jdbc:mysql://localhost:3306/paotui</jdbc-url>
      <jdbc-additional-properties>
        <property name="com.intellij.clouds.kubernetes.db.host.port" />
        <property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
        <property name="com.intellij.clouds.kubernetes.db.container.port" />
      </jdbc-additional-properties>
      <working-dir>$ProjectFileDir$</working-dir>
    </data-source>
  </component>
</project>
.idea/encodings.xml
New file
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding">
    <file url="file://$PROJECT_DIR$/pt-admin/src/main/java" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-admin/src/main/resources" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-common/src/main/java" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-common/src/main/resources" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-errand/src/main/java" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-errand/src/main/resources" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-framework/src/main/java" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-framework/src/main/resources" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-system/src/main/java" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/pt-system/src/main/resources" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
  </component>
</project>
.idea/jarRepositories.xml
New file
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="RemoteRepositoriesConfiguration">
    <remote-repository>
      <option name="id" value="public" />
      <option name="name" value="aliyun nexus" />
      <option name="url" value="https://maven.aliyun.com/repository/public" />
    </remote-repository>
    <remote-repository>
      <option name="id" value="central" />
      <option name="name" value="Central Repository" />
      <option name="url" value="https://repo.maven.apache.org/maven2" />
    </remote-repository>
    <remote-repository>
      <option name="id" value="central" />
      <option name="name" value="Maven Central repository" />
      <option name="url" value="https://repo1.maven.org/maven2" />
    </remote-repository>
    <remote-repository>
      <option name="id" value="jboss.community" />
      <option name="name" value="JBoss Community repository" />
      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
    </remote-repository>
  </component>
</project>
.idea/misc.xml
New file
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ExternalStorageConfigurationManager" enabled="true" />
  <component name="MavenProjectsManager">
    <option name="originalFiles">
      <list>
        <option value="$PROJECT_DIR$/pom.xml" />
      </list>
    </option>
  </component>
  <component name="PWA">
    <option name="enabled" value="true" />
    <option name="wasEnabledAtLeastOnce" value="true" />
  </component>
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
</project>
.idea/vcs.xml
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="" vcs="Git" />
  </component>
</project>
pom.xml
New file
@@ -0,0 +1,269 @@
<?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.pt</groupId>
    <artifactId>kunming-daiban</artifactId>
    <version>3.8.9</version>
    <name>kunming-daiban</name>
    <description>若依管理系统</description>
    <properties>
        <ruoyi.version>3.8.9</ruoyi.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
        <spring-boot.version>2.5.15</spring-boot.version>
        <druid.version>1.2.23</druid.version>
        <bitwalker.version>1.21</bitwalker.version>
        <swagger.version>3.0.0</swagger.version>
        <kaptcha.version>2.3.3</kaptcha.version>
        <pagehelper.boot.version>1.4.7</pagehelper.boot.version>
        <fastjson.version>2.0.53</fastjson.version>
        <oshi.version>6.6.5</oshi.version>
        <commons.io.version>2.13.0</commons.io.version>
        <poi.version>4.1.2</poi.version>
        <velocity.version>2.3</velocity.version>
        <jwt.version>0.9.1</jwt.version>
        <!-- override dependency version -->
        <tomcat.version>9.0.98</tomcat.version>
        <logback.version>1.2.13</logback.version>
        <spring-security.version>5.7.12</spring-security.version>
        <spring-framework.version>5.3.39</spring-framework.version>
    </properties>
    <!-- 依赖声明 -->
    <dependencyManagement>
        <dependencies>
            <!-- 覆盖SpringFramework的依赖配置-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-framework-bom</artifactId>
                <version>${spring-framework.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- 覆盖SpringSecurity的依赖配置-->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-bom</artifactId>
                <version>${spring-security.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- SpringBoot的依赖配置-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- 覆盖logback的依赖配置-->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>${logback.version}</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${logback.version}</version>
            </dependency>
            <!-- 覆盖tomcat的依赖配置-->
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-core</artifactId>
                <version>${tomcat.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-el</artifactId>
                <version>${tomcat.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-websocket</artifactId>
                <version>${tomcat.version}</version>
            </dependency>
            <!-- 阿里数据库连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <!-- 解析客户端操作系统、浏览器等 -->
            <dependency>
                <groupId>eu.bitwalker</groupId>
                <artifactId>UserAgentUtils</artifactId>
                <version>${bitwalker.version}</version>
            </dependency>
            <!-- pagehelper 分页插件 -->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>${pagehelper.boot.version}</version>
            </dependency>
            <!-- 获取系统信息 -->
            <dependency>
                <groupId>com.github.oshi</groupId>
                <artifactId>oshi-core</artifactId>
                <version>${oshi.version}</version>
            </dependency>
            <!-- Swagger3依赖 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-boot-starter</artifactId>
                <version>${swagger.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.swagger</groupId>
                        <artifactId>swagger-models</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- io常用工具类 -->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>${commons.io.version}</version>
            </dependency>
            <!-- excel工具 -->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>${poi.version}</version>
            </dependency>
            <!-- velocity代码生成使用模板 -->
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>${velocity.version}</version>
            </dependency>
            <!-- 阿里JSON解析器 -->
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <!-- Token生成与解析-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jwt.version}</version>
            </dependency>
            <!-- 验证码 -->
            <dependency>
                <groupId>pro.fessional</groupId>
                <artifactId>kaptcha</artifactId>
                <version>${kaptcha.version}</version>
            </dependency>
            <!-- 核心模块-->
            <dependency>
                <groupId>com.pt</groupId>
                <artifactId>pt-framework</artifactId>
                <version>${ruoyi.version}</version>
            </dependency>
            <!-- 核心模块-->
            <dependency>
                <groupId>com.pt</groupId>
                <artifactId>pt-errand</artifactId>
                <version>3.8.9</version>
            </dependency>
            <!-- 系统模块-->
            <dependency>
                <groupId>com.pt</groupId>
                <artifactId>pt-system</artifactId>
                <version>${ruoyi.version}</version>
            </dependency>
            <!-- 通用工具-->
            <dependency>
                <groupId>com.pt</groupId>
                <artifactId>pt-common</artifactId>
                <version>${ruoyi.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.34</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <modules>
        <module>pt-admin</module>
        <module>pt-framework</module>
        <module>pt-system</module>
        <module>pt-common</module>
        <module>pt-errand</module>
    </modules>
    <packaging>pom</packaging>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>
pt-admin/pom.xml
New file
@@ -0,0 +1,94 @@
<?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">
    <parent>
        <artifactId>kunming-daiban</artifactId>
        <groupId>com.pt</groupId>
        <version>3.8.9</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>pt-admin</artifactId>
    <description>
        web服务入口
    </description>
    <dependencies>
<!--        &lt;!&ndash; spring-boot-devtools &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-devtools</artifactId>-->
<!--            <optional>true</optional> &lt;!&ndash; 表示依赖不会传递 &ndash;&gt;-->
<!--        </dependency>-->
        <!-- swagger3-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
        </dependency>
        <!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
         <!-- Mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 核心模块-->
        <dependency>
            <groupId>com.pt</groupId>
            <artifactId>pt-framework</artifactId>
        </dependency>
        <dependency>
            <groupId>com.pt</groupId>
            <artifactId>pt-errand</artifactId>
            <version>3.8.9</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
                <configuration>
                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <warName>${project.artifactId}</warName>
                </configuration>
           </plugin>
        </plugins>
        <finalName>${project.artifactId}</finalName>
    </build>
</project>
pt-admin/src/main/java/com/ruoyi/PaoTuiApplication.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
 * 启动程序
 *
 * @author ruoyi
 */
@EnableScheduling
@EnableTransactionManagement
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class PaoTuiApplication {
    public static void main(String[] args) {
        SpringApplication.run(PaoTuiApplication.class, args);
        System.out.println("(♥◠‿◠)ノ゙ 启动成功   ლ(´ڡ`ლ)゙");
    }
}
pt-admin/src/main/java/com/ruoyi/PaoTuiServletInitializer.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
 * web容器中进行部署
 */
public class PaoTuiServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(PaoTuiApplication.class);
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
New file
@@ -0,0 +1,91 @@
package com.ruoyi.web.controller.common;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.service.ISysConfigService;
/**
 * 验证码操作处理
 *
 * @author ruoyi
 */
@Api(value = "验证码",tags = "验证码")
@RestController
public class CaptchaController {
    @Resource(name = "captchaProducer")
    private Producer captchaProducer;
    @Resource(name = "captchaProducerMath")
    private Producer captchaProducerMath;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ISysConfigService configService;
    /**
     * 生成验证码
     */
    @ApiOperation(value = "获取图片验证码", notes = "获取验证码")
    @GetMapping("/captchaImage")
    public AjaxResult getCode(HttpServletResponse response) throws IOException {
        AjaxResult ajax = AjaxResult.success();
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        ajax.put("captchaEnabled", captchaEnabled);
        if (!captchaEnabled) {
            return ajax;
        }
        // 保存验证码信息
        String uuid = IdUtils.simpleUUID();
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
        String capStr = null, code = null;
        BufferedImage image = null;
        // 生成验证码
        String captchaType = RuoYiConfig.getCaptchaType();
        if ("math".equals(captchaType)) {
            String capText = captchaProducerMath.createText();
            capStr = capText.substring(0, capText.lastIndexOf("@"));
            code = capText.substring(capText.lastIndexOf("@") + 1);
            image = captchaProducerMath.createImage(capStr);
        } else if ("char".equals(captchaType)) {
            capStr = code = captchaProducer.createText();
            image = captchaProducer.createImage(capStr);
        }
        System.out.println(code);
        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        // 转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(image, "jpg", os);
        } catch (IOException e) {
            return AjaxResult.error(e.getMessage());
        }
        ajax.put("uuid", uuid);
        ajax.put("img", "data:image/jpg;base64,"+Base64.encode(os.toByteArray()));
        return ajax;
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
New file
@@ -0,0 +1,150 @@
package com.ruoyi.web.controller.common;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.ServerConfig;
/**
 * 通用请求处理
 *
 * @author ruoyi
 */
@RestController
@Api(value = "公共基础接口",tags = "公共基础接口")
@RequestMapping("/common")
public class CommonController {
    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
    @Autowired
    private ServerConfig serverConfig;
    private static final String FILE_DELIMETER = ",";
    /**
     * 通用上传请求(单个)
     */
    @ApiOperation(value = "文件上传", notes = "文件上传")
    @PostMapping("/upload")
    public AjaxResult uploadFile(MultipartFile file) throws Exception {
        try {
            // 上传文件路径
            String filePath = RuoYiConfig.getUploadPath();
            // 上传并返回新文件名称
            String fileName = FileUploadUtils.upload(filePath, file);
            String url = serverConfig.getUrl() + fileName;
            AjaxResult ajax = AjaxResult.success();
            ajax.put("url", url);
            ajax.put("fileName", fileName);
            ajax.put("newFileName", FileUtils.getName(fileName));
            ajax.put("originalFilename", file.getOriginalFilename());
            return ajax;
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }
    /**
     * 通用上传请求(多个)
     */
    @ApiOperation(value = "文件上传(多个)", notes = "文件上传(多个)")
    @PostMapping("/uploads")
    public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception {
        try {
            // 上传文件路径
            String filePath = RuoYiConfig.getUploadPath();
            List<String> urls = new ArrayList<String>();
            List<String> fileNames = new ArrayList<String>();
            List<String> newFileNames = new ArrayList<String>();
            List<String> originalFilenames = new ArrayList<String>();
            for (MultipartFile file : files) {
                // 上传并返回新文件名称
                String fileName = FileUploadUtils.upload(filePath, file);
                String url = serverConfig.getUrl() + fileName;
                urls.add(url);
                fileNames.add(fileName);
                newFileNames.add(FileUtils.getName(fileName));
                originalFilenames.add(file.getOriginalFilename());
            }
            AjaxResult ajax = AjaxResult.success();
            ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
            ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
            ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
            ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
            return ajax;
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }
//    /**
//     * 本地资源通用下载
//     */
//    @GetMapping("/download/resource")
//    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
//            throws Exception {
//        try {
//            if (!FileUtils.checkAllowDownload(resource)) {
//                throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
//            }
//            // 本地资源路径
//            String localPath = RuoYiConfig.getProfile();
//            // 数据库资源地址
//            String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
//            // 下载名称
//            String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
//            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
//            FileUtils.setAttachmentResponseHeader(response, downloadName);
//            FileUtils.writeBytes(downloadPath, response.getOutputStream());
//        } catch (Exception e) {
//            log.error("下载文件失败", e);
//        }
//    }
//    /**
//     * 通用下载请求
//     *
//     * @param fileName 文件名称
//     * @param delete   是否删除
//     */
//    @GetMapping("/download")
//    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) {
//        try {
//            if (!FileUtils.checkAllowDownload(fileName)) {
//                throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
//            }
//            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
//            String filePath = RuoYiConfig.getDownloadPath() + fileName;
//
//            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
//            FileUtils.setAttachmentResponseHeader(response, realFileName);
//            FileUtils.writeBytes(filePath, response.getOutputStream());
//            if (delete) {
//                FileUtils.deleteFile(filePath);
//            }
//        } catch (Exception e) {
//            log.error("下载文件失败", e);
//        }
//    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/AddressBookController.java
New file
@@ -0,0 +1,81 @@
package com.ruoyi.web.controller.errand;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.app.AddAddressBookDTO;
import com.ruoyi.errand.object.dto.app.UpdateAddressBookDTO;
import com.ruoyi.errand.object.vo.app.AddressBookByCommunityIdVO;
import com.ruoyi.errand.object.vo.app.AddressBookListVO;
import com.ruoyi.errand.service.AddressBookService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Validated
@RestController
@RequestMapping(value = "/app/addressBook")
@Api(value = "用户地址簿", tags = "用户地址簿操作控制器")
@Slf4j
public class AddressBookController {
    @Autowired
    private AddressBookService addressBookService;
    /**
     * 地址簿列表,可根据小区id查看
     */
    @GetMapping("/addressBookByCommunityId")
    @ApiOperation(value = "根据小区id查看地址簿列表",tags = "app用户端-下单页")
    public R<List<AddressBookByCommunityIdVO>> addressBookByCommunityId(@RequestParam("communityId")  Integer communityId) {
        return R.ok(addressBookService.addressBookByCommunityId(communityId));
    }
    /**
     * 查看地址簿列表
     */
    @GetMapping("/addressBookList")
    @ApiOperation(value = "查看用户地址簿列表",tags = "app用户端-地址簿")
    public R<List<AddressBookListVO>> addressBookList() {
        return R.ok(addressBookService.addressBookList());
    }
    /**
     * 设置默认地址
     */
    @PutMapping("/setDefaultAddress")
    @ApiOperation(value = "设置默认地址",tags = "app用户端-地址簿")
    public R<Void> setDefaultAddress(@RequestParam("id") Integer id) {
        addressBookService.setDefaultAddress(id);
        return R.ok();
    }
    /**
     * 添加地址
     */
    @PostMapping("/add")
    @ApiOperation(value = "添加新地址",tags = "app用户端-地址簿")
    public R<Void> add(@RequestBody AddAddressBookDTO addAddressBookDTO ) {
        addressBookService.add(addAddressBookDTO);
        return R.ok();
    }
    /**
     * 修改地址
     */
    @PutMapping("/set")
    @ApiOperation(value = "修改地址",tags = "app用户端-地址簿")
    public R<Void> set(@RequestBody UpdateAddressBookDTO updateAddressBookDTO ) {
        addressBookService.set(updateAddressBookDTO);
        return R.ok();
    }
    /**
     * 删除地址
     */
    @DeleteMapping("/delete")
    @ApiOperation(value = "删除地址",tags = "app用户端-地址簿")
    public R<Void> delete(@RequestParam("id") Integer id) {
        addressBookService.delete(id);
        return R.ok();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/AgreementController.java
New file
@@ -0,0 +1,51 @@
package com.ruoyi.web.controller.errand;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.domain.Agreement;
import com.ruoyi.errand.object.dto.app.AgreementDTO;
import com.ruoyi.errand.service.AgreementService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "/app/agreement")
@Api(value = "协议", tags = "协议操作控制器")
@Slf4j
public class AgreementController {
    @Autowired
    private AgreementService agreementService;
    /**
     * 协议 类型(1=用户协议,2=隐私协议,3=下单须知,4=注销协议,5=首页介绍,6=快递代拿下单说明,7=商品代买下单说明)
     */
    @GetMapping("/getAgreementByType")
    @ApiOperation(value = "根据类型获取不同协议",tags = "app用户端-协议")
    public R<Agreement> getAgreementByType(@RequestParam(value = "type") Integer type) {
        return R.ok(agreementService.getAgreementByType(type));
    }
    @PostMapping("/addAgreement")
    @PreAuthorize("@ss.hasPermi('system:agreement:list')")
    @ApiOperation(value = "协议管理-添加", tags = {"管理后台-系统管理"})
    public R<Void> addAgreement(@RequestBody AgreementDTO agreementDTO){
        //先删除启动页的数据
        Agreement one = agreementService.getOne(new LambdaQueryWrapper<Agreement>().eq(Agreement::getType, agreementDTO.getType()));
        if (one!=null){
            agreementService.removeById(one);
        }
        Agreement agreement = new Agreement();
        BeanUtils.copyProperties(agreementDTO,agreement);
        agreementService.save(agreement);
        return R.ok();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/AppUserController.java
New file
@@ -0,0 +1,317 @@
package com.ruoyi.web.controller.errand;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.errand.domain.Order;
import com.ruoyi.errand.object.dto.app.AppletLogin;
import com.ruoyi.errand.object.dto.app.BirthDayDTO;
import com.ruoyi.errand.object.dto.app.MobileLoginDTO;
import com.ruoyi.errand.object.dto.app.RegisterDTO;
import com.ruoyi.errand.object.dto.sys.AppUserPageListDTO;
import com.ruoyi.errand.object.dto.sys.OrderPageListDTO;
import com.ruoyi.errand.object.dto.sys.UserStatsDTO;
import com.ruoyi.errand.object.vo.app.AppUserInfoVO;
import com.ruoyi.errand.object.vo.app.OrderPageVO;
import com.ruoyi.errand.object.vo.app.UserTopInfoVO;
import com.ruoyi.errand.object.vo.login.LoginVO;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.vo.sys.*;
import com.ruoyi.errand.service.AppUserService;
import com.ruoyi.errand.utils.RefundCallbackResult;
import com.ruoyi.errand.utils.TokenBlacklistService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.*;
@RestController
@RequestMapping(value = "/app/user")
@Api(value = "app用户",tags = "app用户操作控制器")
@Slf4j
public class AppUserController {
    @Autowired
    private AppUserService appUserService;
    @Autowired
    private TokenBlacklistService blacklistService;
    @GetMapping("/test")
    @ApiOperation(value = "登出" ,tags = "app用户端")
    public R<Void> test(@RequestHeader("Authorization") String token) {
        throw new ServiceException("测试");
    }
    /**
     * 登出
     */
    @GetMapping("/logout")
    @ApiOperation(value = "登出" ,tags = "app用户端")
    public R<Void> logout(@RequestHeader("Authorization") String token) {
        // 1. 将令牌加入黑名单
        blacklistService.addToBlacklist(token);
        return R.ok();
    }
    /**
     * 获取短信验证码
     */
    @GetMapping("/getSMSCode")
    @ApiOperation(value = "获取短信验证码",tags = "app用户端")
    public R<Void> getSMSCode(@RequestParam("phone") String phone) {
        appUserService.getSMSCode(phone);
        return R.ok();
    }
    /**
     * 手机号验证码登录
     */
    @PostMapping("/mobileLogin")
    @ApiOperation(value = "手机号登录",tags = "app用户端")
    public R<LoginVO> mobileLogin(@RequestBody @Valid MobileLoginDTO mobileLogin) {
        return appUserService.mobileLogin(mobileLogin);
    }
    /**
     * 小程序一键登录
     */
    @PostMapping("/appletLogin")
    @ApiOperation(value = "小程序一键登录",tags = "app用户端")
    public R<LoginVO> appletLogin(@RequestBody @Valid AppletLogin appletLogin) {
        return appUserService.appletLogin(appletLogin);
    }
    /**
     * 注册成功-修改用户成功
     */
    @PostMapping("/register")
    @ApiOperation(value = "注册",tags = "app用户端")
    public R<Void> register(@RequestBody @Valid RegisterDTO registerDTO) {
        appUserService.register(registerDTO);
        return R.ok();
    }
    /**
     * 根据小区id(可选择)
     * 获取小区名字 以及对应的收货地址 小区价格
     */
    @GetMapping("/getOrderPage")
    @ApiOperation(value = "获取相关信息",tags = "app用户端-下单页面")
    public R<OrderPageVO> getOrderPage(@RequestParam(value = "communityId",required = false) Integer communityId) {
        return R.ok(appUserService.getOrderPage(communityId));
    }
    /**
     * 个人中心
     */
    @GetMapping("/getMyInfo")
    @ApiOperation(value = "获取个人信息",tags = "app用户端-个人信息")
    public R<AppUserInfoVO> getMyInfo() {
        return R.ok(appUserService.getMyInfo());
    }
    /**
     * 选择性别 1=男,2=女,3=未知
     */
    @PutMapping("/setSex")
    @ApiOperation(value = "修改性别",tags = "app用户端-个人信息")
    public R<Void> setSex(@RequestParam Integer sex) {
        appUserService.setSex(sex);
        return R.ok();
    }
    /**
     * 修改生日
     */
    @PutMapping("/setBirthDay")
    @ApiOperation(value = "修改生日",tags = "app用户端-个人信息")
    public R<Void> setBirthDay(@RequestBody BirthDayDTO birth) {
        appUserService.setBirthDay(birth);
        return R.ok();
    }
    /**
     * 注销账号 需要修改delFlag?
     */
    @DeleteMapping("/delete")
    @ApiOperation(value = "注销账号",tags = "app用户端-个人信息")
    public R<Void> delete() {
        appUserService.delete();
        return R.ok();
    }
    /**
     * 用户统计
     */
    @GetMapping("/userTopInfo")
    @PreAuthorize("@ss.hasPermi('system:index:statistics')")
    @ApiOperation(value = "用户统计-顶部数据", tags = "系统后台-首页")
    public R<UserTopInfoVO> userTopInfo() {
        LocalDateTime[] dateRange;//日期范围
        dateRange = new LocalDateTime[]{
                LocalDateTime.now().with(LocalTime.MIN),
                LocalDateTime.now().with(LocalTime.MAX)
        };
        return R.ok(appUserService.userTopInfo(dateRange[0], dateRange[1]));
    }
    /**
     * 用户统计
     */
    @PostMapping("/statistics")
    @PreAuthorize("@ss.hasPermi('system:index:statistics')")
    @ApiOperation(value = "用户统计-折线图", tags = "系统后台-首页")
    public R<UserStatsVO> statistics(@RequestBody @Valid UserStatsDTO userStatsDTO) {
        LocalDateTime[] dateRange;//日期范围
        String datePattern;//日期格式
        switch (userStatsDTO.getType()) {
            case 1: // 今日 当天数据,按小时划分
                dateRange = new LocalDateTime[]{
                        LocalDateTime.now().with(LocalTime.MIN),
                        LocalDateTime.now().with(LocalTime.MAX)
                };
                datePattern = "HH时";
                break;
            case 2: // 本周 按星期一、二...划分
                LocalDate now = LocalDate.now();
                dateRange = new LocalDateTime[]{
                        now.with(DayOfWeek.MONDAY).atStartOfDay(), // 本周一
                        now.with(DayOfWeek.SUNDAY).atTime(LocalTime.MAX) // 本周日
                };
                datePattern = "EEEE";
                break;
            case 3: // 本月 按1日至月末划分
                YearMonth currentMonth = YearMonth.now();
                dateRange = new LocalDateTime[]{
                        currentMonth.atDay(1).atStartOfDay(), // 本月1日
                        currentMonth.atEndOfMonth().atTime(LocalTime.MAX) // 本月最后一天
                };
                datePattern = "dd日";
                break;
            case 4: // 本季度 按当前季度的月份划分
                YearMonth thisMonth = YearMonth.now();
                YearMonth firstMonthOfQuarter = thisMonth.with(
                        Month.of(((thisMonth.getMonthValue() - 1) / 3) * 3 + 1));
                YearMonth lastMonthOfQuarter = firstMonthOfQuarter.plusMonths(2);
                dateRange = new LocalDateTime[]{
                        firstMonthOfQuarter.atDay(1).atStartOfDay(), // 季度第一个月1日
                        lastMonthOfQuarter.atEndOfMonth().atTime(LocalTime.MAX) // 季度最后一个月最后一天
                };
                datePattern = "MM月";
                break;
            case 5: // 半年 上半年或下半年完整月份
                YearMonth current = YearMonth.now();
                YearMonth halfYearStart = current.getMonthValue() <= 6 ?
                        YearMonth.of(current.getYear(), Month.JANUARY) :
                        YearMonth.of(current.getYear(), Month.JULY);
                YearMonth halfYearEnd = halfYearStart.plusMonths(5);
                dateRange = new LocalDateTime[]{
                        halfYearStart.atDay(1).atStartOfDay(),
                        halfYearEnd.atEndOfMonth().atTime(LocalTime.MAX)
                };
                datePattern = "MM月";
                break;
            case 6: // 本年 按1月至12月完整年份
                int year = Year.now().getValue();
                dateRange = new LocalDateTime[]{
                        LocalDateTime.of(year, 1, 1, 0, 0), // 1月1日
                        LocalDateTime.of(year, 12, 31, 23, 59, 59) // 12月31日
                };
                datePattern = "MM月";
                break;
            case 7: // 自定义 按起始时间到终止时间之间的日期划分
                if (userStatsDTO.getStartDate() == null || userStatsDTO.getEndDate() == null) {
                    throw new ServiceException("自定义时间范围必须指定开始和结束日期");
                }
                if (userStatsDTO.getStartDate().isAfter(userStatsDTO.getEndDate())) {
                    throw new ServiceException("开始日期不能晚于结束日期");
                }
                dateRange = new LocalDateTime[]{
                        userStatsDTO.getStartDate().atStartOfDay(),
                        userStatsDTO.getEndDate().atTime(LocalTime.MAX)
                };
                datePattern = "yyyy-MM-dd";
                break;
            default:
                throw new ServiceException("无效的筛选类型: " + userStatsDTO.getType());
        }
        return R.ok(appUserService.getUserStats(dateRange[0], dateRange[1], datePattern));
    }
    /**
     * 用户管理列表
     */
    @PostMapping("/list")
    @PreAuthorize("@ss.hasPermi('system:appuser:list')")
    @ApiOperation(value = "用户管理-分页列表", tags = "系统后台-用户管理")
    public R<IPage<AppUserPageListVO>> getAppUserPageList(@RequestBody @Valid AppUserPageListDTO appUserPageListDTO) {
        return R.ok(appUserService.getAppUserPageList(appUserPageListDTO));
    }
    /**
     * 查看详情
     */
    @GetMapping("/detail")
    @PreAuthorize("@ss.hasPermi('system:appuser:list')")
    @ApiOperation(value = "用户管理-用户详情", tags = "系统后台-用户管理")
    public R<AppUserSysDetailVO> detail(@RequestParam("id") Integer id) {
        return R.ok(appUserService.detail(id));
    }
    /**
     * 冻结/解冻
     */
    @PutMapping("/froze")
    @PreAuthorize("@ss.hasPermi('system:appuser:list')")
    @ApiOperation(value = "用户管理-冻结/解冻", tags = "系统后台-用户管理")
    public R froze(@RequestParam("id") Integer id) {
        appUserService.froze(id);
        return R.ok();
    }
    /**
     * 会员退费
     */
    @GetMapping("/refund")
    @PreAuthorize("@ss.hasPermi('system:appuser:list')")
    @ApiOperation(value = "用户管理-会员退费", tags = "系统后台-用户管理")
    public R<Void> refund(@RequestParam("id") Integer id) {
        appUserService.refund(id);
        return R.ok();
    }
    /**
     * 订单取消支付回退
     *
     * @param refundCallbackResult
     * @param response
     * @return
     */
    @ResponseBody
    @GetMapping("/refundPayMoneyCallback")
    public void refundPayMoneyCallback(RefundCallbackResult refundCallbackResult, HttpServletResponse response) {
        R callback = appUserService.refundPayMoneyCallback(refundCallbackResult);
        if (callback.getCode() == 200) {
            response.setStatus(200);
            PrintWriter out = null;
            try {
                out = response.getWriter();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            out.println("success");
            out.flush();
            out.close();
        }
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/BannerController.java
New file
@@ -0,0 +1,97 @@
package com.ruoyi.web.controller.errand;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.domain.Banner;
import com.ruoyi.errand.object.dto.sys.AddBannerDTO;
import com.ruoyi.errand.object.dto.sys.OrderPageListDTO;
import com.ruoyi.errand.object.vo.app.BannerVO;
import com.ruoyi.errand.object.vo.sys.BannerDetailVo;
import com.ruoyi.errand.object.vo.sys.BannerPageListVO;
import com.ruoyi.errand.object.vo.sys.EditBannerDTO;
import com.ruoyi.errand.object.vo.sys.OrderPageListVO;
import com.ruoyi.errand.service.BannerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@Validated
@RestController
@RequestMapping(value = "/app/banner")
@Api(value = "banner", tags = "banner操作控制器")
@Slf4j
public class BannerController {
    @Autowired
    private BannerService bannerService;
    /**
     * banner 广告查看
     */
    @GetMapping("/getBannerList")
    @ApiOperation(value = "获取banner列表",tags = "app用户端-下单页")
    public R<List<BannerVO>> getBannerList() {
        return R.ok(bannerService.getBannerList());
    }
    /**
     * 分页查询
     */
    @GetMapping("/list")
    @PreAuthorize("@ss.hasPermi('system:banner:list')")
    @ApiOperation(value = "banner管理-分页查询", tags = {"管理后台-系统管理"})
    public R<IPage<BannerPageListVO>> pageList(@RequestParam("pageNum") Integer pageNum,
                                              @RequestParam("pageSize") Integer pageSize,
                                              @RequestParam("name") String name) {
        IPage<BannerPageListVO> iPage=new Page<>(pageNum,pageSize);
        return R.ok(bannerService.pageList(iPage,name));
    }
    /**
     * 添加
     */
    @PostMapping("/add")
    @PreAuthorize("@ss.hasPermi('system:banner:list')")
    @ApiOperation(value = "banner管理-添加", tags = {"管理后台-系统管理"})
    public R add(@RequestBody AddBannerDTO addBannerDTO) {
        bannerService.add(addBannerDTO);
        return R.ok();
    }
    /**
     * 编辑
     */
    @PutMapping("/edit")
    @PreAuthorize("@ss.hasPermi('system:banner:list')")
    @ApiOperation(value = "banner管理-添加", tags = {"管理后台-系统管理"})
    public R edit(@RequestBody EditBannerDTO editBannerDTO) {
        bannerService.edit(editBannerDTO);
        return R.ok();
    }
    /**
     * 删除
     */
    @DeleteMapping("/delete")
    @PreAuthorize("@ss.hasPermi('system:banner:list')")
    @ApiOperation(value = "banner管理-删除", tags = {"管理后台-系统管理"})
    public R delete(@RequestParam("id")Integer id) {
        bannerService.delete(id);
        return R.ok();
    }
    /**
     * 查看详情
     */
    @GetMapping("/detail")
    @PreAuthorize("@ss.hasPermi('system:banner:list')")
    @ApiOperation(value = "banner管理-查看详情", tags = {"管理后台-系统管理"})
    public R<BannerDetailVo> detail(@RequestParam("id")Integer id) {
        return R.ok(bannerService.detail(id));
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityController.java
New file
@@ -0,0 +1,129 @@
package com.ruoyi.web.controller.errand;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.domain.Community;
import com.ruoyi.errand.object.dto.sys.*;
import com.ruoyi.errand.object.vo.app.CommunityListVO;
import com.ruoyi.errand.object.vo.sys.*;
import com.ruoyi.errand.service.CommunityService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping(value = "/app/community")
@Api(value = "小区信息", tags = "小区信息操作控制器")
@Slf4j
public class CommunityController {
    @Autowired
    private CommunityService communityService;
    /**
     * 区域内的小区列表
     */
    @GetMapping("/getCommunity")
    @ApiOperation(value = "区域内的小区列表",tags = "app用户端-小区")
    public R<List<CommunityListVO>> getCommunity(@RequestParam(value = "regionId") Integer regionId) {
        return communityService.getCommunity(regionId);
    }
    /**
     * 获取所有小区列表
     */
    @GetMapping("/getTotalCommunityList")
    @PreAuthorize("@ss.hasPermi('system:index:statistics')")
    @ApiOperation(value = "获取所有小区列表(开通的小区总数取列表大小吧)",tags = "系统后台-首页")
    public R<List<CommunityListVO>> getTotalCommunityList() {
        return communityService.getTotalCommunityList();
    }
    /**
     * 加载未绑定跑腿员的小区  权限设置
     */
    @GetMapping("/getAllCommunityList")
    @PreAuthorize("@ss.hasPermi('system:appuser:list')")
    @ApiOperation(value = "跑腿员管理-加载未绑定跑腿员的小区", tags = "系统后台-跑腿员管理")
    public R<List<AllCommunityListVO>> getAllCommunityList() {
        return R.ok(communityService.getAllCommunityList());
    }
    /**
     * 小区管理列表查看 权限设置
     */
    @PostMapping("/list")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "小区管理-分页列表", tags = "系统后台-小区管理")
    public R<IPage<CommunityPageListVO>> getCommunityPageList(@RequestBody @Valid CommunityPageListDTO communityPageListDTO) {
        return R.ok(communityService.getCommunityPageList(communityPageListDTO));
    }
    /**
     * 添加小区 权限设置
     * 判断小区是否存在
     * 判断跑腿员是否存在
     * 判断跑腿员是否绑定
     *
     */
    @PostMapping("/add")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "小区管理-添加", tags = "系统后台-小区管理")
    public R<Void> add(@RequestBody @Valid AddCommunityDTO addCommunityDTO) {
        communityService.add(addCommunityDTO);
        return R.ok();
    }
    /**
     * 编辑 权限设置
     */
    @PutMapping("/edit")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "小区管理-编辑", tags = "系统后台-小区管理")
    public R<Void> edit(@RequestBody @Valid EditCommunityDTO editCommunityDTO) {
        communityService.edit(editCommunityDTO);
        return R.ok();
    }
    /**
     * 删除 权限设置
     * 删除小区 跑腿员 app用户
     */
    @DeleteMapping("/delete")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "小区管理-删除", tags = "系统后台-小区管理")
    public R<Void> delete(@RequestParam("id")Integer id) {
        communityService.delete(id);
        return R.ok();
    }
    /**
     * 冻结 权限设置
     */
    @PutMapping("/froze")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "小区管理-冻结/解冻", tags = "系统后台-小区管理")
    public R<Void> froze(@RequestParam("id")Integer id) {
        communityService.froze(id);
        return R.ok();
    }
    /**
     * 查看详情 权限设置
     */
    @GetMapping("/detail")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "小区管理-查看详情", tags = "系统后台-小区管理")
    public R<CommunitySysDetailVO> detail(@RequestParam("id") Integer id) {
        return R.ok(communityService.detail(id));
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityCourierController.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.web.controller.errand;
import com.ruoyi.errand.service.CommunityCourierService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Validated
@RestController
@RequestMapping(value = "/communityCourier")
@Api(value = "小区 - 跑腿员关联", tags = "小区 - 跑腿员关联操作控制器")
@Slf4j
public class CommunityCourierController {
    @Autowired
    private CommunityCourierService communityCourierService;
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/CourierController.java
New file
@@ -0,0 +1,156 @@
package com.ruoyi.web.controller.errand;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.sys.AddCourierDTO;
import com.ruoyi.errand.object.dto.sys.AppUserPageListDTO;
import com.ruoyi.errand.object.dto.sys.CourierPageListDTO;
import com.ruoyi.errand.object.dto.sys.EditCourierDTO;
import com.ruoyi.errand.object.vo.app.*;
import com.ruoyi.errand.object.vo.sys.*;
import com.ruoyi.errand.service.CourierService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.analysis.function.Add;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping(value = "/app/courier")
@Api(value = "跑腿员信息", tags = "跑腿员信息操作控制器")
@Slf4j
public class CourierController {
    @Autowired
    private CourierService courierService;
    /**
     * 工作台-基础信息
     */
    @GetMapping("/getCourierInfo")
    @ApiOperation(value = "工作台-基础信息",tags = "app用户端-跑腿员")
    public R<CourierInfoVO> getCourierInfo() {
        return R.ok(courierService.getCourierInfo());
    }
    /**
     * 工作台-数据统计
     */
    @GetMapping("/getDatStatistics")
    @ApiOperation(value = "工作台-数据统计",tags = "app用户端-跑腿员")
    public R<CourierStatisticsVO> getDatStatistics() {
        return R.ok(courierService.getDatStatistics());
    }
    /**
     * 订单列表  1待确认2进行中3已取消4已完成
     */
    @GetMapping("/getCourierOrderList")
    @ApiOperation(value = "订单列表",tags = "app用户端-跑腿员")
    public R<IPage<CourierOrderListVO>> getCourierOrderList(
            @RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum,
            @RequestParam(value = "pageSize",defaultValue = "10") Integer pageSize,
            @RequestParam(value = "orderStatus",required = false) Integer orderStatus) {
        return R.ok(courierService.getCourierOrderList(pageNum,pageSize,orderStatus));
    }
    /**
     * 接单
     */
    @PutMapping("/receiveOrder")
    @ApiOperation(value = "接单",tags = "app用户端-跑腿员")
    public R<Void> receiveOrder(@RequestParam("id") Integer id) {
        courierService.receiveOrder(id);
        return R.ok();
    }
    /**
     * 完成订单
     */
    @PutMapping("/completeOrder")
    @ApiOperation(value = "完成订单",tags = "app用户端-跑腿员")
    public R<Void> completeOrder(@RequestBody @Valid CompleteOrderDTO completeOrderDTO) {
        courierService.completeOrder(completeOrderDTO);
        return R.ok();
    }
    /**
     * 加载未绑定小区的跑腿员   权限设置
     */
    @GetMapping("/getAllCourierList")
    @PreAuthorize("@ss.hasPermi('system:appuser:list')")
    @ApiOperation(value = "小区管理-加载未绑定小区的跑腿员", tags = "系统后台-小区管理")
    public R<List<AllCourierListVO>> getAllCourierList() {
        return R.ok(courierService.getAllCourierList());
    }
    /**
     * 跑腿员管理列表 权限设置
     */
    @PostMapping("/list")
    @PreAuthorize("@ss.hasPermi('system:courier:list')")
    @ApiOperation(value = "跑腿员管理-分页列表", tags = "系统后台-跑腿员管理")
    public R<IPage<CourierPageListVO>> getCourierPageList(@RequestBody @Valid CourierPageListDTO courierPageListDTO) {
        return R.ok(courierService.getCourierPageList(courierPageListDTO));
    }
    /**
     * 查看详情 权限设置
     */
    @GetMapping("/detail")
    @PreAuthorize("@ss.hasPermi('system:courier:list')")
    @ApiOperation(value = "跑腿员管理-查看详情", tags = "系统后台-跑腿员管理")
    public R<CourierSysDetailVO> detail(@RequestParam("id") Integer id) {
        return R.ok(courierService.detail(id));
    }
    /**
     * 添加 权限设置
     */
    @PostMapping("/add")
    @PreAuthorize("@ss.hasPermi('system:courier:list')")
    @ApiOperation(value = "跑腿员管理-添加", tags = "系统后台-跑腿员管理")
    public R<Void> add(@RequestBody @Valid AddCourierDTO addCourierDTO) {
        courierService.add(addCourierDTO);
        return R.ok();
    }
    /**
     * 编辑 权限设置
     */
    @PutMapping("/edit")
    @PreAuthorize("@ss.hasPermi('system:courier:list')")
    @ApiOperation(value = "跑腿员管理-编辑", tags = "系统后台-跑腿员管理")
    public R<Void> edit(@RequestBody @Valid EditCourierDTO editCourierDTO) {
        courierService.edit(editCourierDTO);
        return R.ok();
    }
    /**
     * 删除 权限设置
     */
    @DeleteMapping("/delete")
    @PreAuthorize("@ss.hasPermi('system:courier:list')")
    @ApiOperation(value = "跑腿员管理-删除", tags = "系统后台-跑腿员管理")
    public R<Void> delete(@RequestParam("id")Integer id) {
        courierService.delete(id);
        return R.ok();
    }
    /**
     * 复职/离职 权限设置
     */
    @PutMapping("/froze")
    @PreAuthorize("@ss.hasPermi('system:courier:list')")
    @ApiOperation(value = "跑腿员管理-复职/离职", tags = "系统后台-跑腿员管理")
    public R<Void> froze(@RequestParam("id")Integer id) {
        courierService.froze(id);
        return R.ok();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/EvaluationController.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.web.controller.errand;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.app.AddEvaluationDTO;
import com.ruoyi.errand.service.EvaluationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@Validated
@RestController
@RequestMapping(value = "/app/evaluation")
@Api(value = "评价表", tags = "评价表操作控制器")
@Slf4j
public class EvaluationController {
    @Autowired
    private EvaluationService evaluationService;
    /**
     * 订单评价
     */
    @PostMapping("/add")
    @ApiOperation(value = "评价订单",tags = "app用户端-订单")
    public R<Void> add(@RequestBody @Valid AddEvaluationDTO addEvaluationDTO) {
        evaluationService.add(addEvaluationDTO);
        return R.ok();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/FeedbackController.java
New file
@@ -0,0 +1,71 @@
package com.ruoyi.web.controller.errand;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.sys.FeedbackPageListDTO;
import com.ruoyi.errand.object.dto.sys.OrderPageListDTO;
import com.ruoyi.errand.object.vo.sys.FeedbackPageListVO;
import com.ruoyi.errand.object.vo.sys.OrderPageListVO;
import com.ruoyi.errand.service.FeedbackService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.parameters.P;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Validated
@RestController
@RequestMapping(value = "/app/feedback")
@Api(value = "用户反馈", tags = "用户反馈操作控制器")
@Slf4j
public class FeedbackController {
    @Autowired
    private FeedbackService feedbackService;
    /**
     * 意见反馈
     */
    @PostMapping("/add")
    @ApiOperation(value = "意见反馈",tags = "app用户端-意见反馈")
    public R<Integer> add(@RequestParam String content) {
        feedbackService.add(content);
        return R.ok();
    }
    /**
     * 分页列表
     */
    @PostMapping("/list")
    @PreAuthorize("@ss.hasPermi('system:feedback:list')")
    @ApiOperation(value = "反馈管理-分页列表", tags = "系统后台-订单管理")
    public R<IPage<FeedbackPageListVO>> getFeedbackPageList(@RequestBody @Valid FeedbackPageListDTO feedbackPageListDTO) {
        return R.ok(feedbackService.getFeedbackPageList(feedbackPageListDTO));
    }
    /**
     * 删除
     */
    @DeleteMapping("/delete")
    @PreAuthorize("@ss.hasPermi('system:feedback:list')")
    @ApiOperation(value = "反馈管理-删除", tags = "系统后台-订单管理")
    public R delete(@RequestParam("id")Integer id) {
        feedbackService.delete(id);
        return R.ok();
    }
    /**
     * 处理
     */
    @PutMapping("/dispose")
    @PreAuthorize("@ss.hasPermi('system:feedback:list')")
    @ApiOperation(value = "反馈管理-处理", tags = "系统后台-订单管理")
    public R dispose(@RequestParam("id")Integer id) {
        feedbackService.dispose(id);
        return R.ok();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/OrderController.java
New file
@@ -0,0 +1,299 @@
package com.ruoyi.web.controller.errand;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.errand.domain.Order;
import com.ruoyi.errand.object.dto.app.ConfirmOrderDTO;
import com.ruoyi.errand.object.dto.app.OrderStatsDTO;
import com.ruoyi.errand.object.dto.app.OrderStatsVO;
import com.ruoyi.errand.object.dto.app.SetConfirmOrderDTO;
import com.ruoyi.errand.object.dto.sys.FinanceStatisticsDTO;
import com.ruoyi.errand.object.dto.sys.OrderPageListDTO;
import com.ruoyi.errand.object.dto.sys.UserStatsDTO;
import com.ruoyi.errand.object.vo.app.*;
import com.ruoyi.errand.object.vo.sys.FinanceStatisticsVO;
import com.ruoyi.errand.object.vo.sys.OrderPageListVO;
import com.ruoyi.errand.object.vo.sys.OrderSysDetailVO;
import com.ruoyi.errand.object.vo.sys.UserStatsVO;
import com.ruoyi.errand.service.OrderService;
import com.ruoyi.errand.utils.RefundCallbackResult;
import com.ruoyi.errand.utils.UniPayCallbackResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.*;
import java.util.List;
@RestController
@RequestMapping(value = "/app/order")
@Api(value = "订单表", tags = "订单表操作控制器")
@Slf4j
public class OrderController {
    @Autowired
    private OrderService orderService;
    /**
     * 下单
     */
    @PostMapping("/confirmOrder")
    @ApiOperation(value = "确认订单",tags = "app用户端-下单页")
    public R<ConfirmOrderVO> confirmOrder(@RequestBody @Valid ConfirmOrderDTO confirmOrderDTO) {
        return R.ok(orderService.confirmOrder(confirmOrderDTO));
    }
    /**
     * 支付  需再次校验会员是否到期  是否设置规定时间?
     */
    @PostMapping("/orderPayment")
    @ApiOperation(value = "订单支付", tags = {"app用户端-订单支付"})
    public R orderPayment(@RequestBody @Valid ConfirmOrderDTO confirmOrderDTO){
        return orderService.orderPayment(confirmOrderDTO);
    }
    /**
     * 订单支付回调通知
     */
    @ResponseBody
    @GetMapping("/orderPaymentCallback")
    public void orderPaymentCallback(UniPayCallbackResult uniPayCallbackResult, HttpServletResponse response){
        String jsonString = JSONObject.toJSONString(uniPayCallbackResult);
        log.info("订单支付回调json:{}", jsonString);
        R callback = orderService.orderPaymentCallback(uniPayCallbackResult);
        if(callback.getCode() == 200){
            response.setStatus(200);
            PrintWriter out = null;
            try {
                out = response.getWriter();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            out.println("success");
            out.flush();
            out.close();
        }
    }
    /**
     * 订单列表  1待确认2进行中3已取消4已完成
     */
    @GetMapping("/getAppUserOrderList")
    @ApiOperation(value = "订单列表",tags = "app用户端-订单页面")
    public R<IPage<AppUserOrderListVO>> getAppUserOrderList(
            @RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum,
            @RequestParam(value = "pageSize",defaultValue = "10") Integer pageSize,
            @RequestParam(value = "orderStatus",required = false) Integer orderStatus) {
        return R.ok(orderService.getAppUserOrderList(pageNum,pageSize,orderStatus));
    }
    /**
     * 查看订单详情
     */
    @GetMapping("/getOrderDetail")
    @ApiOperation(value = "查看订单详情",tags = {"app用户端-订单页面","app用户端-跑腿员"})
    public R<OrderDetailVO> getOrderDetail(@RequestParam(value = "id") Integer id) {
        return R.ok(orderService.getOrderDetail(id));
    }
    /**
     * 修改订单信息 (在跑腿接单前,修改了还要提示跑腿)
     */
    @PutMapping("/setOrderInfo")
    @ApiOperation(value = "修改订单信息",tags = "app用户端-订单页面")
    public R<Void> setOrderInfo(@RequestBody @Valid SetConfirmOrderDTO setConfirmOrderDTO) {
        orderService.setOrderInfo(setConfirmOrderDTO);
        return R.ok();
    }
    /**
     * 取消订单 判断状态 回退金额
     */
    @PutMapping("/cancelOrder")
    @ApiOperation(value = "取消订单",tags = "app用户端-订单页面")
    public R<Void> cancelOrder(@RequestParam(value = "id") Integer id) {
        orderService.cancelOrder(id);
        return R.ok();
    }
    /**
     * 订单取消支付回退
     *
     * @param refundCallbackResult
     * @param response
     * @return
     */
    @ResponseBody
    @GetMapping("/refundPayMoneyCallback")
    public void refundPayMoneyCallback(RefundCallbackResult refundCallbackResult, HttpServletResponse response) {
        R callback = orderService.refundPayMoneyCallback(refundCallbackResult);
        if (callback.getCode() == 200) {
            response.setStatus(200);
            PrintWriter out = null;
            try {
                out = response.getWriter();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            out.println("success");
            out.flush();
            out.close();
        }
    }
    /**
     * 订单统计
     */
    @GetMapping("/orderTopInfo")
    @PreAuthorize("@ss.hasPermi('system:index:statistics')")
    @ApiOperation(value = "订单统计-顶部数据", tags = "系统后台-首页")
    public R<OrderTopInfoVO> orderTopInfo(@RequestParam(value = "communityId" ,required = false) Integer communityId) {
        return R.ok(orderService.orderTopInfo(communityId==null?0:communityId));
    }
    /**
     * 订单统计
     */
    @PostMapping("/statistics")
    @PreAuthorize("@ss.hasPermi('system:index:statistics')")
    @ApiOperation(value = "订单统计-折线图", tags = "系统后台-首页")
    public R<OrderStatsVO> statistics(@RequestBody @Valid OrderStatsDTO orderStatsDTO) {
        LocalDateTime[] dateRange;//日期范围
        String datePattern;//日期格式
        switch (orderStatsDTO.getType()) {
            case 1: // 今日 当天数据,按小时划分
                dateRange = new LocalDateTime[]{
                        LocalDateTime.now().with(LocalTime.MIN),
                        LocalDateTime.now().with(LocalTime.MAX)
                };
                datePattern = "HH时";
                break;
            case 2: // 本周 按星期一、二...划分
                LocalDate now = LocalDate.now();
                dateRange = new LocalDateTime[]{
                        now.with(DayOfWeek.MONDAY).atStartOfDay(), // 本周一
                        now.with(DayOfWeek.SUNDAY).atTime(LocalTime.MAX) // 本周日
                };
                datePattern = "EEEE";
                break;
            case 3: // 本月 按1日至月末划分
                YearMonth currentMonth = YearMonth.now();
                dateRange = new LocalDateTime[]{
                        currentMonth.atDay(1).atStartOfDay(), // 本月1日
                        currentMonth.atEndOfMonth().atTime(LocalTime.MAX) // 本月最后一天
                };
                datePattern = "dd日";
                break;
            case 4: // 本季度 按当前季度的月份划分
                YearMonth thisMonth = YearMonth.now();
                YearMonth firstMonthOfQuarter = thisMonth.with(
                        Month.of(((thisMonth.getMonthValue() - 1) / 3) * 3 + 1));
                YearMonth lastMonthOfQuarter = firstMonthOfQuarter.plusMonths(2);
                dateRange = new LocalDateTime[]{
                        firstMonthOfQuarter.atDay(1).atStartOfDay(), // 季度第一个月1日
                        lastMonthOfQuarter.atEndOfMonth().atTime(LocalTime.MAX) // 季度最后一个月最后一天
                };
                datePattern = "MM月";
                break;
            case 5: // 半年 上半年或下半年完整月份
                YearMonth current = YearMonth.now();
                YearMonth halfYearStart = current.getMonthValue() <= 6 ?
                        YearMonth.of(current.getYear(), Month.JANUARY) :
                        YearMonth.of(current.getYear(), Month.JULY);
                YearMonth halfYearEnd = halfYearStart.plusMonths(5);
                dateRange = new LocalDateTime[]{
                        halfYearStart.atDay(1).atStartOfDay(),
                        halfYearEnd.atEndOfMonth().atTime(LocalTime.MAX)
                };
                datePattern = "MM月";
                break;
            case 6: // 本年 按1月至12月完整年份
                int year = Year.now().getValue();
                dateRange = new LocalDateTime[]{
                        LocalDateTime.of(year, 1, 1, 0, 0), // 1月1日
                        LocalDateTime.of(year, 12, 31, 23, 59, 59) // 12月31日
                };
                datePattern = "MM月";
                break;
            case 7: // 自定义 按起始时间到终止时间之间的日期划分
                if (orderStatsDTO.getStartDate() == null || orderStatsDTO.getEndDate() == null) {
                    throw new ServiceException("自定义时间范围必须指定开始和结束日期");
                }
                if (orderStatsDTO.getStartDate().isAfter(orderStatsDTO.getEndDate())) {
                    throw new ServiceException("开始日期不能晚于结束日期");
                }
                dateRange = new LocalDateTime[]{
                        orderStatsDTO.getStartDate().atStartOfDay(),
                        orderStatsDTO.getEndDate().atTime(LocalTime.MAX)
                };
                datePattern = "yyyy-MM-dd";
                break;
            default:
                throw new ServiceException("无效的筛选类型: " + orderStatsDTO.getType());
        }
        return R.ok(orderService.getOrderStats(dateRange[0], dateRange[1], datePattern,orderStatsDTO.getCommunityId()));
    }
    /**
     * 财务统计列表
     */
    @PostMapping("/financeStatistics")
    @PreAuthorize("@ss.hasPermi('system:finance:statistics')")
    @ApiOperation(value = "财务统计-分页列表", tags = "系统后台-首页")
    public R<IPage<FinanceStatisticsVO>> financeStatistics(@RequestBody @Valid FinanceStatisticsDTO financeStatisticsDTO) {
        return R.ok(orderService.financeStatistics(financeStatisticsDTO));
    }
    /**
     * 导出
     */
    @PostMapping("/financeStatistics/export")
    @PreAuthorize("@ss.hasPermi('system:finance:statistics')")
    @ApiOperation(value = "财务统计-导出", tags = "系统后台-首页")
    public void export(HttpServletResponse response,@RequestBody @Valid FinanceStatisticsDTO financeStatisticsDTO) {
        List<FinanceStatisticsVO> exportList=orderService.export(financeStatisticsDTO);
        ExcelUtil<FinanceStatisticsVO> util = new ExcelUtil<FinanceStatisticsVO>(FinanceStatisticsVO.class);
        util.exportExcel(response, exportList, "财务统计");
    }
    /**
     * 订单管理列表
     */
    @PostMapping("/list")
    @PreAuthorize("@ss.hasPermi('system:order:list')")
    @ApiOperation(value = "订单管理-分页列表", tags = "系统后台-订单管理")
    public R<IPage<OrderPageListVO>> getOrderPageList(@RequestBody @Valid OrderPageListDTO orderPageListDTO) {
        return R.ok(orderService.getOrderPageList(orderPageListDTO));
    }
    /**
     * 查看详情
     */
    @GetMapping("/detail")
    @PreAuthorize("@ss.hasPermi('system:order:list')")
    @ApiOperation(value = "订单管理-订单详情", tags = "系统后台-订单管理")
    public R<OrderSysDetailVO> detail(@RequestParam("id") Integer id) {
        return R.ok(orderService.detail(id));
    }
    /**
     * 导出
     */
    @PostMapping("/list/export")
    @PreAuthorize("@ss.hasPermi('system:order:list')")
    @ApiOperation(value = "订单管理-导出", tags = "系统后台-订单管理")
    public void orderExport(HttpServletResponse response,@RequestBody @Valid OrderPageListDTO orderPageListDTO) {
        List<OrderPageListVO> exportList=orderService.orderExport(orderPageListDTO);
        ExcelUtil<OrderPageListVO> util = new ExcelUtil<OrderPageListVO>(OrderPageListVO.class);
        util.exportExcel(response, exportList, "订单管理");
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/PhoneController.java
New file
@@ -0,0 +1,42 @@
package com.ruoyi.web.controller.errand;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.vo.app.ConfirmOrderVO;
import com.ruoyi.errand.service.PhoneService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Validated
@RestController
@RequestMapping(value = "/app/phone")
@Api(value = "客服电话", tags = "客服电话操作控制器")
@Slf4j
public class PhoneController {
    @Autowired
    private PhoneService phoneService;
    /**
     * 联系客服
     */
    @GetMapping("/getServletPhone")
    @ApiOperation(value = "获取客服电话",tags = "app用户端-联系客服")
    public R<String> getServletPhone() {
        return R.ok(phoneService.getServletPhone());
    }
    /**
     * 保存客服电话
     */
    @PutMapping("/saveServicePhone")
    @PreAuthorize("@ss.hasPermi('system:feedback:list')")
    @ApiOperation(value = "反馈管理-保存客服电话", tags = {"管理后台-系统管理"})
    public R<Void> saveServicePhone(@RequestParam("phone")String phone) {
        phoneService.saveServicePhone(phone);
        return R.ok();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/RegionController.java
New file
@@ -0,0 +1,45 @@
package com.ruoyi.web.controller.errand;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.domain.Region;
import com.ruoyi.errand.service.RegionService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@Validated
@RestController
@RequestMapping(value = "/app/region")
@Api(value = "省市区三级联动", tags = "省市区三级联动操作控制器")
@Slf4j
public class RegionController {
    @Autowired
    private RegionService regionService;
    /**
     * 省市区级联
     */
    @GetMapping("/getProvinceList1")
    @ApiOperation(value = "省市区级联",tags = {"app用户端"})
    public R<List<Region>> getProvinceList1() {
        List<Region> list = regionService.list();
        List<Region> collect = list.stream().filter(s -> s.getParentId() == 0).collect(Collectors.toList());
        for (Region region : collect) {
            List<Region> collect1 = list.stream().filter(s -> s.getParentId().equals(region.getId())).collect(Collectors.toList());
            for (Region region1 : collect1) {
                List<Region> collect2 = list.stream().filter(s -> s.getParentId().equals(region1.getId())).collect(Collectors.toList());
                region1.setChilds(collect2);
            }
            region.setChilds(collect1);
        }
        return R.ok(collect);
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/ReportController.java
New file
@@ -0,0 +1,73 @@
package com.ruoyi.web.controller.errand;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.domain.Report;
import com.ruoyi.errand.object.dto.app.AddReportDTO;
import com.ruoyi.errand.object.dto.sys.CourierPageListDTO;
import com.ruoyi.errand.object.dto.sys.ReportPageListDTO;
import com.ruoyi.errand.object.vo.sys.CourierPageListVO;
import com.ruoyi.errand.object.vo.sys.ReportPageListVO;
import com.ruoyi.errand.service.ReportService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Validated
@RestController
@RequestMapping(value = "/app/report")
@Api(value = "上报表", tags = "上报表操作控制器")
@Slf4j
public class ReportController {
    @Autowired
    private ReportService reportService;
    /**
     * 上报不存在小区
     */
    @PostMapping("/add")
    @ApiOperation(value = "上报不存在小区",tags = "app用户端-上报")
    public R<Void> add(@RequestBody @Valid AddReportDTO addReportDTO) {
        reportService.add(addReportDTO);
        return R.ok();
    }
    /**
     * 未开通上报列表 权限设置
     */
    @PostMapping("/list")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "未开通上报管理-列表", tags = "系统后台-小区管理")
    public R<IPage<ReportPageListVO>> getReportList(@RequestBody @Valid ReportPageListDTO reportPageListDTO) {
        return R.ok(reportService.getReportList(reportPageListDTO));
    }
    /**
     * 处理  权限设置
     */
    @PutMapping("/dispose")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "未开通上报管理-处理", tags = "系统后台-小区管理")
    public R<Void> dispose(@RequestParam("id")Integer id) {
        reportService.dispose(id);
        return R.ok();
    }
    /**
     * 删除 权限设置
     */
    @DeleteMapping("/delete")
    @PreAuthorize("@ss.hasPermi('system:community:list')")
    @ApiOperation(value = "未开通上报管理-删除", tags = "系统后台-小区管理")
    public R<Void> delete(@RequestParam("id")Integer id) {
        reportService.delete(id);
        return R.ok();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/SystemConfigController.java
New file
@@ -0,0 +1,57 @@
package com.ruoyi.web.controller.errand;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.constant.SystemConfigTypeConstant;
import com.ruoyi.errand.domain.SystemConfig;
import com.ruoyi.errand.object.dto.app.StartPageSetDto;
import com.ruoyi.errand.service.SystemConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Validated
@RestController
@RequestMapping(value = "/app/systemConfig")
@Api(value = "系统配置", tags = "系统配置操作控制器")
@Slf4j
public class SystemConfigController {
    @Autowired
    private SystemConfigService systemConfigService;
    @PostMapping("/startPage/add")
    @PreAuthorize("@ss.hasPermi('system:agreement:list')")
    @ApiOperation(value = "协议管理-启动页配置", tags = {"管理后台-系统管理"})
    public R<Void> startPageAdd(@RequestBody StartPageSetDto startPageSetDto){
        //先删除启动页的数据
        List<SystemConfig> list = systemConfigService.lambdaQuery().eq(SystemConfig::getType, SystemConfigTypeConstant.START_PAGE).list();
        systemConfigService.removeBatchByIds(list);
        SystemConfig  systemConfig = new SystemConfig();
        systemConfig.setType(SystemConfigTypeConstant.START_PAGE);
        systemConfig.setContent(JSON.toJSONString(startPageSetDto));
        systemConfigService.save(systemConfig);
        return R.ok();
    }
    @GetMapping("/index/start")
    @ApiOperation(value = "启动页-详情", tags = {"app用户端-启动页","管理后台-启动页配置"})
    public R<StartPageSetDto> indexStart(){
        SystemConfig one = systemConfigService.lambdaQuery().eq(SystemConfig::getType, SystemConfigTypeConstant.START_PAGE).one();
        if (one==null){
            return R.ok();
        }
        StartPageSetDto indexConfigSetDto = JSONObject.parseObject(one.getContent(), StartPageSetDto.class);
        return R.ok(indexConfigSetDto);
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/UserCancellationLogController.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.web.controller.errand;
import com.ruoyi.errand.service.UserCancellationLogService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Validated
@RestController
@RequestMapping(value = "/userCancellationLog")
@Api(value = "注销记录", tags = "注销记录操作控制器")
@Slf4j
public class UserCancellationLogController {
    @Autowired
    private UserCancellationLogService userCancellationLogService;
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipOrderController.java
New file
@@ -0,0 +1,62 @@
package com.ruoyi.web.controller.errand;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.app.VipPaymentDTO;
import com.ruoyi.errand.service.VipOrderService;
import com.ruoyi.errand.utils.UniPayCallbackResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.PrintWriter;
@RestController
@RequestMapping(value = "/app/vipOrder")
@Api(value = "vip订单信息", tags = "vip订单信息操作控制器")
@Slf4j
public class VipOrderController {
    @Autowired
    private VipOrderService vipOrderService;
    /**
     * 购买会员
     */
    @PostMapping("/vipPayment")
    @ApiOperation(value = "购买会员",tags = "app用户端-成为会员")
    public R vipPayment(@RequestBody @Valid VipPaymentDTO vipPaymentDTO) {
        return vipOrderService.vipPayment(vipPaymentDTO);
    }
    /**
     * 订单支付回调通知
     */
    @ResponseBody
    @GetMapping("/orderPaymentCallback")
    public void orderPaymentCallback(UniPayCallbackResult uniPayCallbackResult, HttpServletResponse response){
        String jsonString = JSONObject.toJSONString(uniPayCallbackResult);
        log.info("订单支付回调json:{}", jsonString);
        R callback = vipOrderService.orderPaymentCallback(uniPayCallbackResult);
        if(callback.getCode() == 200){
            response.setStatus(200);
            PrintWriter out = null;
            try {
                out = response.getWriter();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            out.println("success");
            out.flush();
            out.close();
        }
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipSettingController.java
New file
@@ -0,0 +1,61 @@
package com.ruoyi.web.controller.errand;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.sys.SetPriceDTO;
import com.ruoyi.errand.object.vo.app.VipInfoListVO;
import com.ruoyi.errand.service.VipSettingService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@Validated
@RestController
@RequestMapping(value = "/app/vipSetting")
@Api(value = "会员权益", tags = "会员权益操作控制器")
@Slf4j
public class VipSettingController {
    @Autowired
    private VipSettingService vipSettingService;
    /**
     * 获取会员列表
     */
    @GetMapping("/getVipInfoList")
    @ApiOperation(value = "获取会员信息列表",tags = "app用户端-成为会员")
    public R<List<VipInfoListVO>> getVipInfoList() {
        return R.ok(vipSettingService.getVipInfoList());
    }
    /**
     * 设置价格
     */
    @PostMapping("/setPrice")
    @PreAuthorize("@ss.hasPermi('system:vip:list')")
    @ApiOperation(value = "会员管理-设置价格", tags = "系统后台-订单管理")
    public R setPrice(@RequestBody @Valid SetPriceDTO setPriceDTO) {
        vipSettingService.setPrice(setPriceDTO);
        return R.ok();
    }
    /**
     * 查看详情
     */
    @GetMapping("/getVipList")
    @PreAuthorize("@ss.hasPermi('system:vip:list')")
    @ApiOperation(value = "会员管理-列表", tags = "系统后台-订单管理")
    public R<List<VipInfoListVO>> getVipList() {
        return R.ok(vipSettingService.getVipInfoList());
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
New file
@@ -0,0 +1,114 @@
//package com.ruoyi.web.controller.monitor;
//
//import java.util.ArrayList;
//import java.util.Collection;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//import java.util.Properties;
//import java.util.Set;
//import java.util.TreeSet;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.data.redis.core.RedisCallback;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.security.access.prepost.PreAuthorize;
//import org.springframework.web.bind.annotation.DeleteMapping;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.PathVariable;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//import com.ruoyi.common.constant.CacheConstants;
//import com.ruoyi.common.core.domain.AjaxResult;
//import com.ruoyi.common.utils.StringUtils;
//import com.ruoyi.system.domain.SysCache;
//
///**
// * 缓存监控
// *
// * @author ruoyi
// */
//@RestController
//@RequestMapping("/monitor/cache")
//public class CacheController {
//    @Autowired
//    private RedisTemplate<String, String> redisTemplate;
//
//    private final static List<SysCache> caches = new ArrayList<SysCache>();
//
//    static {
//        caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
//        caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
//        caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
//        caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
//        caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
//        caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
//        caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
//    }
//
//    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
//    @GetMapping()
//    public AjaxResult getInfo() throws Exception {
//        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
//        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
//        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
//
//        Map<String, Object> result = new HashMap<>(3);
//        result.put("info", info);
//        result.put("dbSize", dbSize);
//
//        List<Map<String, String>> pieList = new ArrayList<>();
//        commandStats.stringPropertyNames().forEach(key -> {
//            Map<String, String> data = new HashMap<>(2);
//            String property = commandStats.getProperty(key);
//            data.put("name", StringUtils.removeStart(key, "cmdstat_"));
//            data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
//            pieList.add(data);
//        });
//        result.put("commandStats", pieList);
//        return AjaxResult.success(result);
//    }
//
//    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
//    @GetMapping("/getNames")
//    public AjaxResult cache() {
//        return AjaxResult.success(caches);
//    }
//
//    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
//    @GetMapping("/getKeys/{cacheName}")
//    public AjaxResult getCacheKeys(@PathVariable String cacheName) {
//        Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
//        return AjaxResult.success(new TreeSet<>(cacheKeys));
//    }
//
//    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
//    @GetMapping("/getValue/{cacheName}/{cacheKey}")
//    public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) {
//        String cacheValue = redisTemplate.opsForValue().get(cacheKey);
//        SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue);
//        return AjaxResult.success(sysCache);
//    }
//
//    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
//    @DeleteMapping("/clearCacheName/{cacheName}")
//    public AjaxResult clearCacheName(@PathVariable String cacheName) {
//        Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*");
//        redisTemplate.delete(cacheKeys);
//        return AjaxResult.success();
//    }
//
//    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
//    @DeleteMapping("/clearCacheKey/{cacheKey}")
//    public AjaxResult clearCacheKey(@PathVariable String cacheKey) {
//        redisTemplate.delete(cacheKey);
//        return AjaxResult.success();
//    }
//
//    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
//    @DeleteMapping("/clearCacheAll")
//    public AjaxResult clearCacheAll() {
//        Collection<String> cacheKeys = redisTemplate.keys("*");
//        redisTemplate.delete(cacheKeys);
//        return AjaxResult.success();
//    }
//}
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java
New file
@@ -0,0 +1,25 @@
//package com.ruoyi.web.controller.monitor;
//
//import org.springframework.security.access.prepost.PreAuthorize;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//import com.ruoyi.common.core.domain.AjaxResult;
//import com.ruoyi.framework.web.domain.Server;
//
///**
// * 服务器监控
// *
// * @author ruoyi
// */
//@RestController
//@RequestMapping("/monitor/server")
//public class ServerController {
//    @PreAuthorize("@ss.hasPermi('monitor:server:list')")
//    @GetMapping()
//    public AjaxResult getInfo() throws Exception {
//        Server server = new Server();
//        server.copyTo();
//        return AjaxResult.success(server);
//    }
//}
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
New file
@@ -0,0 +1,77 @@
package com.ruoyi.web.controller.monitor;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPasswordService;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.service.ISysLogininforService;
/**
 * 系统访问记录
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/logininfor")
public class SysLogininforController extends BaseController {
    @Autowired
    private ISysLogininforService logininforService;
    @Autowired
    private SysPasswordService passwordService;
    @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysLogininfor logininfor) {
        startPage();
        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
        return getDataTable(list);
    }
    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysLogininfor logininfor) {
        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
        ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
        util.exportExcel(response, list, "登录日志");
    }
    @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
    @Log(title = "登录日志", businessType = BusinessType.DELETE)
    @DeleteMapping("/{infoIds}")
    public AjaxResult remove(@PathVariable Long[] infoIds) {
        return toAjax(logininforService.deleteLogininforByIds(infoIds));
    }
    @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
    @Log(title = "登录日志", businessType = BusinessType.CLEAN)
    @DeleteMapping("/clean")
    public AjaxResult clean() {
        logininforService.cleanLogininfor();
        return success();
    }
    @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
    @Log(title = "账户解锁", businessType = BusinessType.OTHER)
    @GetMapping("/unlock/{userName}")
    public AjaxResult unlock(@PathVariable("userName") String userName) {
        passwordService.clearLoginRecordCache(userName);
        return success();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
New file
@@ -0,0 +1,67 @@
package com.ruoyi.web.controller.monitor;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysOperLogService;
/**
 * 操作日志记录
 *
 * @author ruoyi
 */
@Api(value = "日志管理",tags = "日志管理")
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController {
    @Autowired
    private ISysOperLogService operLogService;
    @PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysOperLog operLog) {
        startPage();
        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
        return getDataTable(list);
    }
    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysOperLog operLog) {
        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
        ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
        util.exportExcel(response, list, "操作日志");
    }
    @Log(title = "操作日志", businessType = BusinessType.DELETE)
    @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
    @DeleteMapping("/{operIds}")
    public AjaxResult remove(@PathVariable Long[] operIds) {
        return toAjax(operLogService.deleteOperLogByIds(operIds));
    }
    @Log(title = "操作日志", businessType = BusinessType.CLEAN)
    @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
    @DeleteMapping("/clean")
    public AjaxResult clean() {
        operLogService.cleanOperLog();
        return success();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
New file
@@ -0,0 +1,72 @@
package com.ruoyi.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysUserOnline;
import com.ruoyi.system.service.ISysUserOnlineService;
/**
 * 在线用户监控
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController {
    @Autowired
    private ISysUserOnlineService userOnlineService;
    @Autowired
    private RedisCache redisCache;
    @PreAuthorize("@ss.hasPermi('monitor:online:list')")
    @GetMapping("/list")
    public TableDataInfo list(String ipaddr, String userName) {
        Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
        List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
        for (String key : keys) {
            LoginUser user = redisCache.getCacheObject(key);
            if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
                userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
            } else if (StringUtils.isNotEmpty(ipaddr)) {
                userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
            } else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) {
                userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
            } else {
                userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
            }
        }
        Collections.reverse(userOnlineList);
        userOnlineList.removeAll(Collections.singleton(null));
        return getDataTable(userOnlineList);
    }
    /**
     * 强退用户
     */
    @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
    @Log(title = "在线用户", businessType = BusinessType.FORCE)
    @DeleteMapping("/{tokenId}")
    public AjaxResult forceLogout(@PathVariable String tokenId) {
        redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
        return success();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
New file
@@ -0,0 +1,122 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
//import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.service.ISysConfigService;
/**
 * 参数配置 信息操作处理
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/config")
public class SysConfigController extends BaseController {
    @Autowired
    private ISysConfigService configService;
    /**
     * 获取参数配置列表
     */
    @PreAuthorize("@ss.hasPermi('system:config:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysConfig config) {
        startPage();
        List<SysConfig> list = configService.selectConfigList(config);
        return getDataTable(list);
    }
    @Log(title = "参数管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:config:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysConfig config) {
        List<SysConfig> list = configService.selectConfigList(config);
//        ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
//        util.exportExcel(response, list, "参数数据");
    }
    /**
     * 根据参数编号获取详细信息
     */
    @PreAuthorize("@ss.hasPermi('system:config:query')")
    @GetMapping(value = "/{configId}")
    public AjaxResult getInfo(@PathVariable Long configId) {
        return success(configService.selectConfigById(configId));
    }
    /**
     * 根据参数键名查询参数值
     */
    @GetMapping(value = "/configKey/{configKey}")
    public AjaxResult getConfigKey(@PathVariable String configKey) {
        return success(configService.selectConfigByKey(configKey));
    }
    /**
     * 新增参数配置
     */
    @PreAuthorize("@ss.hasPermi('system:config:add')")
    @Log(title = "参数管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysConfig config) {
        if (!configService.checkConfigKeyUnique(config)) {
            return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
        }
        config.setCreateBy(getUsername());
        return toAjax(configService.insertConfig(config));
    }
    /**
     * 修改参数配置
     */
    @PreAuthorize("@ss.hasPermi('system:config:edit')")
    @Log(title = "参数管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysConfig config) {
        if (!configService.checkConfigKeyUnique(config)) {
            return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
        }
        config.setUpdateBy(getUsername());
        return toAjax(configService.updateConfig(config));
    }
    /**
     * 删除参数配置
     */
    @PreAuthorize("@ss.hasPermi('system:config:remove')")
    @Log(title = "参数管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{configIds}")
    public AjaxResult remove(@PathVariable Long[] configIds) {
        configService.deleteConfigByIds(configIds);
        return success();
    }
    /**
     * 刷新参数缓存
     */
    @PreAuthorize("@ss.hasPermi('system:config:remove')")
    @Log(title = "参数管理", businessType = BusinessType.CLEAN)
    @DeleteMapping("/refreshCache")
    public AjaxResult refreshCache() {
        configService.resetConfigCache();
        return success();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
New file
@@ -0,0 +1,133 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.errand.object.dto.sys.FinanceStatisticsDTO;
import com.ruoyi.errand.object.vo.sys.FinanceStatisticsVO;
import com.ruoyi.system.object.vo.SysDeptPageVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysDeptService;
import javax.validation.Valid;
/**
 * 部门信息
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/dept")
@ApiModel("权限管理-部门")
public class SysDeptController extends BaseController {
    @Autowired
    private ISysDeptService deptService;
    @GetMapping("/page")
    @PreAuthorize("@ss.hasPermi('system:dept:list')")
    @ApiOperation(value = "部门管理-分页列表", tags = "系统后台-权限管理")
    public R<IPage<SysDeptPageVO>> page(@RequestParam(value = "pageNum",defaultValue = "0")Integer pageNum,
                                        @RequestParam(value = "pageSize",defaultValue="10")Integer pageSize,
                                        @RequestParam(value = "name",required = false)String name) {
        IPage<SysDeptPageVO> iPage = new Page<>(pageNum, pageSize);
        return R.ok(deptService.page(iPage,name));
    }
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @ApiOperation(value = "用户管理-部门选择框", tags = "系统后台-权限管理")
    @GetMapping("/list")
    public AjaxResult list() {
        List<SysDeptPageVO> list = deptService.getDeptList();
        return success(list);
    }
    /**
     * 获取部门列表
     */
  /*  @PreAuthorize("@ss.hasPermi('system:dept:list')")
    @GetMapping("/list")
    public AjaxResult list(SysDept dept) {
        List<SysDept> depts = deptService.selectDeptList(dept);
        return success(depts);
    }*/
    /**
     * 查询部门列表(排除节点)
     */
 /*   @PreAuthorize("@ss.hasPermi('system:dept:list')")
    @GetMapping("/list/exclude/{deptId}")
    public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
        List<SysDept> depts = deptService.selectDeptList(new SysDept());
        depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
        return success(depts);
    }*/
    /**
     * 根据部门编号获取详细信息
     */
 /*   @PreAuthorize("@ss.hasPermi('system:dept:query')")
    @GetMapping(value = "/{deptId}")
    public AjaxResult getInfo(@PathVariable Long deptId) {
        deptService.checkDeptDataScope(deptId);
        return success(deptService.selectDeptById(deptId));
    }
*/
    /**
     * 新增部门
     */
    @PreAuthorize("@ss.hasPermi('system:dept:list')")
    @Log(title = "部门管理", businessType = BusinessType.INSERT)
    @ApiOperation(value = "部门管理-新增部门", tags = "系统后台-权限管理")
    @GetMapping("/add")
    public AjaxResult add(@RequestParam("name")String name) {
        deptService.add(name);
        return success();
    }
    /**
     * 修改部门
     */
    @PreAuthorize("@ss.hasPermi('system:dept:list')")
    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
    @ApiOperation(value = "部门管理-新增部门", tags = "系统后台-权限管理")
    @PutMapping("/edit")
    public AjaxResult edit(@RequestParam("id")Long id,
                           @RequestParam("name")String name) {
        //查看id是否存在
        SysDept sysDept = deptService.selectDeptById(id);
        if (sysDept==null){
            throw new ServiceException("该部门未找到");
        }
        //修改部门名称
        sysDept.setDeptName(name);
        deptService.updateDeptName(sysDept);
        return success();
    }
    /**
     * 删除部门
     */
    @PreAuthorize("@ss.hasPermi('system:dept:list')")
    @Log(title = "部门管理", businessType = BusinessType.DELETE)
    @ApiOperation(value = "部门管理-删除部门", tags = "系统后台-权限管理")
    @DeleteMapping("/{deptId}")
    public AjaxResult remove(@PathVariable Long deptId) {
        return toAjax(deptService.deleteDeptById(deptId));
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
New file
@@ -0,0 +1,120 @@
package com.ruoyi.web.controller.system;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysDictTypeService;
/**
 * 数据字典信息
 *
 * @author ruoyi
 */
@Api(value = "字典控制器",tags = "字典控制器")
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController {
    @Autowired
    private ISysDictDataService dictDataService;
    @Autowired
    private ISysDictTypeService dictTypeService;
    @PreAuthorize("@ss.hasPermi('system:dict:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysDictData dictData) {
        startPage();
        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
        return getDataTable(list);
    }
    @Log(title = "字典数据", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:dict:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysDictData dictData) {
        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
        ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
        util.exportExcel(response, list, "字典数据");
    }
    /**
     * 查询字典数据详细
     */
    @ApiOperation(value = "获取字典", notes = "获取字典")
    @PreAuthorize("@ss.hasPermi('system:dict:query')")
    @GetMapping(value = "/{dictCode}")
    public AjaxResult getInfo(@PathVariable Long dictCode) {
        return success(dictDataService.selectDictDataById(dictCode));
    }
    /**
     * 根据字典类型查询字典数据信息
     */
    @ApiOperation(value = "根据字典类型查询字典数据信息", notes = "根据字典类型查询字典数据信息")
    @GetMapping(value = "/type/{dictType}")
    public AjaxResult dictType(@PathVariable String dictType) {
        List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
        if (StringUtils.isNull(data)) {
            data = new ArrayList<SysDictData>();
        }
        return success(data);
    }
    /**
     * 新增字典类型
     */
    @PreAuthorize("@ss.hasPermi('system:dict:add')")
    @Log(title = "字典数据", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysDictData dict) {
        dict.setCreateBy(getUsername());
        return toAjax(dictDataService.insertDictData(dict));
    }
    /**
     * 修改保存字典类型
     */
    @PreAuthorize("@ss.hasPermi('system:dict:edit')")
    @Log(title = "字典数据", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysDictData dict) {
        dict.setUpdateBy(getUsername());
        return toAjax(dictDataService.updateDictData(dict));
    }
    /**
     * 删除字典类型
     */
    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
    @Log(title = "字典类型", businessType = BusinessType.DELETE)
    @DeleteMapping("/{dictCodes}")
    public AjaxResult remove(@PathVariable Long[] dictCodes) {
        dictDataService.deleteDictDataByIds(dictCodes);
        return success();
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
New file
@@ -0,0 +1,121 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDictTypeService;
/**
 * 数据字典信息
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController extends BaseController {
    @Autowired
    private ISysDictTypeService dictTypeService;
    @PreAuthorize("@ss.hasPermi('system:dict:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysDictType dictType) {
        startPage();
        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
        return getDataTable(list);
    }
    @Log(title = "字典类型", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:dict:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysDictType dictType) {
        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
        ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
        util.exportExcel(response, list, "字典类型");
    }
    /**
     * 查询字典类型详细
     */
    @PreAuthorize("@ss.hasPermi('system:dict:query')")
    @GetMapping(value = "/{dictId}")
    public AjaxResult getInfo(@PathVariable Long dictId) {
        return success(dictTypeService.selectDictTypeById(dictId));
    }
    /**
     * 新增字典类型
     */
    @PreAuthorize("@ss.hasPermi('system:dict:add')")
    @Log(title = "字典类型", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysDictType dict) {
        if (!dictTypeService.checkDictTypeUnique(dict)) {
            return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
        }
        dict.setCreateBy(getUsername());
        return toAjax(dictTypeService.insertDictType(dict));
    }
    /**
     * 修改字典类型
     */
    @PreAuthorize("@ss.hasPermi('system:dict:edit')")
    @Log(title = "字典类型", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysDictType dict) {
        if (!dictTypeService.checkDictTypeUnique(dict)) {
            return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
        }
        dict.setUpdateBy(getUsername());
        return toAjax(dictTypeService.updateDictType(dict));
    }
    /**
     * 删除字典类型
     */
    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
    @Log(title = "字典类型", businessType = BusinessType.DELETE)
    @DeleteMapping("/{dictIds}")
    public AjaxResult remove(@PathVariable Long[] dictIds) {
        dictTypeService.deleteDictTypeByIds(dictIds);
        return success();
    }
    /**
     * 刷新字典缓存
     */
    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
    @Log(title = "字典类型", businessType = BusinessType.CLEAN)
    @DeleteMapping("/refreshCache")
    public AjaxResult refreshCache() {
        dictTypeService.resetDictCache();
        return success();
    }
    /**
     * 获取字典选择框列表
     */
    @GetMapping("/optionselect")
    public AjaxResult optionselect() {
        List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
        return success(dictTypes);
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java
New file
@@ -0,0 +1,29 @@
package com.ruoyi.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.StringUtils;
/**
 * 首页
 *
 * @author ruoyi
 */
@RestController
public class SysIndexController {
    /**
     * 系统基础配置
     */
    @Autowired
    private RuoYiConfig ruoyiConfig;
    /**
     * 访问首页,提示语
     */
    @RequestMapping("/")
    public String index() {
        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
New file
@@ -0,0 +1,125 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.Set;
import com.ruoyi.system.object.dto.SetPasswordDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysMenuService;
import javax.servlet.http.HttpServletRequest;
/**
 * 登录验证
 *
 * @author ruoyi
 */
@Api(value = "登录控制器",tags = "登录控制器")
@RestController
public class SysLoginController {
    @Autowired
    private SysLoginService loginService;
    @Autowired
    private ISysMenuService menuService;
    @Autowired
    private SysPermissionService permissionService;
    @Autowired
    private TokenService tokenService;
    /**
     * 登录方法
     *
     * @param loginBody 登录信息
     * @return 结果
     */
    @ApiOperation(value = "登录", notes = "登录")
    @PostMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody) {
        AjaxResult ajax = AjaxResult.success();
        // 生成令牌
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
                loginBody.getUuid());
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }
    @ApiOperation(value = "修改密码", notes = "修改密码")
    @PostMapping("/setPassword")
    public AjaxResult setPassword(@RequestBody SetPasswordDTO setPasswordDTO) {
        //修改密码
        loginService.setPassword(setPasswordDTO);
        return AjaxResult.success();
    }
    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @ApiOperation(value = "获取用户信息", notes = "获取用户信息")
    @GetMapping("/getInfo")
    public AjaxResult getInfo() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(user);
        if (!loginUser.getPermissions().equals(permissions)) {
            loginUser.setPermissions(permissions);
            tokenService.refreshToken(loginUser);
        }
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
    }
    /**
     * 获取路由信息
     *
     * @return 路由信息
     */
    @ApiOperation(value = "获取路由信息", notes = "获取路由信息")
    @GetMapping("getRouters")
    public AjaxResult getRouters() {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return AjaxResult.success(menuService.buildMenus(menus));
    }
    /**
     * 退出登录
     */
    @PostMapping("/logout")
    @ApiOperation(value = "退出登录", notes = "用户退出系统")
    public AjaxResult logout(HttpServletRequest request) {
        // 获取当前登录用户
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (loginUser != null) {
            // 删除用户缓存记录
            tokenService.delLoginUser(loginUser.getToken());
        }
        return AjaxResult.success("退出成功");
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java
New file
@@ -0,0 +1,140 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.system.object.vo.MenuTreeVO;
import com.ruoyi.system.object.vo.SysRolePageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysMenuService;
/**
 * 菜单信息
 * */
@Api(value = "菜单控制器",tags = "菜单控制器")
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController {
    @Autowired
    private ISysMenuService menuService;
    /**
     * 获取菜单列表树
     */
    @GetMapping("/tree")
    @ApiOperation(value = "角色管理-菜单列表树", tags = {"系统后台-权限管理","首页"})
    public R<List<MenuTreeVO>> tree() {
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return R.ok(menuService.tree(loginUser.getUserId()));
    }
    /**
     * 获取菜单列表
     */
  /*  @PreAuthorize("@ss.hasPermi('system:role:list')")
    @GetMapping("/list")
    public AjaxResult list(SysMenu menu) {
        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
        return success(menus);
    }*/
    /**
     * 根据菜单编号获取详细信息
     */
/*
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @GetMapping(value = "/{menuId}")
    public AjaxResult getInfo(@PathVariable Long menuId) {
        return success(menuService.selectMenuById(menuId));
    }
*/
    /**
     * 获取菜单下拉树列表
     */
 /*   @GetMapping("/treeselect")
    public AjaxResult treeselect(SysMenu menu) {
        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
        return success(menuService.buildMenuTreeSelect(menus));
    }*/
    /**
     * 加载对应角色菜单列表树
     */
/*
    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
    public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
        List<SysMenu> menus = menuService.selectMenuList(getUserId());
        AjaxResult ajax = AjaxResult.success();
        ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
        ajax.put("menus", menuService.buildMenuTreeSelect(menus));
        return ajax;
    }
*/
    /**
     * 新增菜单
     */
/*    @PreAuthorize("@ss.hasPermi('system:menu:add')")
    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysMenu menu) {
        if (!menuService.checkMenuNameUnique(menu)) {
            return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
            return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
        }
        menu.setCreateBy(getUsername());
        return toAjax(menuService.insertMenu(menu));
    }*/
    /**
     * 修改菜单
     */
/*    @PreAuthorize("@ss.hasPermi('system:menu:edit')")
    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysMenu menu) {
        if (!menuService.checkMenuNameUnique(menu)) {
            return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
            return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
        } else if (menu.getMenuId().equals(menu.getParentId())) {
            return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
        }
        menu.setUpdateBy(getUsername());
        return toAjax(menuService.updateMenu(menu));
    }*/
    /**
     * 删除菜单
     */
 /*   @PreAuthorize("@ss.hasPermi('system:menu:remove')")
    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{menuId}")
    public AjaxResult remove(@PathVariable("menuId") Long menuId) {
        if (menuService.hasChildByMenuId(menuId)) {
            return warn("存在子菜单,不允许删除");
        }
        if (menuService.checkMenuExistRole(menuId)) {
            return warn("菜单已分配,不允许删除");
        }
        return toAjax(menuService.deleteMenuById(menuId));
    }*/
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
New file
@@ -0,0 +1,89 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.SysNotice;
import com.ruoyi.system.service.ISysNoticeService;
/**
 * 公告 信息操作处理
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/notice")
public class SysNoticeController extends BaseController {
    @Autowired
    private ISysNoticeService noticeService;
    /**
     * 获取通知公告列表
     */
    @PreAuthorize("@ss.hasPermi('system:notice:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysNotice notice) {
        startPage();
        List<SysNotice> list = noticeService.selectNoticeList(notice);
        return getDataTable(list);
    }
    /**
     * 根据通知公告编号获取详细信息
     */
    @PreAuthorize("@ss.hasPermi('system:notice:query')")
    @GetMapping(value = "/{noticeId}")
    public AjaxResult getInfo(@PathVariable Long noticeId) {
        return success(noticeService.selectNoticeById(noticeId));
    }
    /**
     * 新增通知公告
     */
    @PreAuthorize("@ss.hasPermi('system:notice:add')")
    @Log(title = "通知公告", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysNotice notice) {
        notice.setCreateBy(getUsername());
        return toAjax(noticeService.insertNotice(notice));
    }
    /**
     * 修改通知公告
     */
    @PreAuthorize("@ss.hasPermi('system:notice:edit')")
    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysNotice notice) {
        notice.setUpdateBy(getUsername());
        return toAjax(noticeService.updateNotice(notice));
    }
    /**
     * 删除通知公告
     */
    @PreAuthorize("@ss.hasPermi('system:notice:remove')")
    @Log(title = "通知公告", businessType = BusinessType.DELETE)
    @DeleteMapping("/{noticeIds}")
    public AjaxResult remove(@PathVariable Long[] noticeIds) {
        return toAjax(noticeService.deleteNoticeByIds(noticeIds));
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
New file
@@ -0,0 +1,115 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.service.ISysPostService;
/**
 * 岗位信息操作处理
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/post")
public class SysPostController extends BaseController {
    @Autowired
    private ISysPostService postService;
    /**
     * 获取岗位列表
     */
    @PreAuthorize("@ss.hasPermi('system:post:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysPost post) {
        startPage();
        List<SysPost> list = postService.selectPostList(post);
        return getDataTable(list);
    }
    @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:post:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysPost post) {
        List<SysPost> list = postService.selectPostList(post);
        ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
        util.exportExcel(response, list, "岗位数据");
    }
    /**
     * 根据岗位编号获取详细信息
     */
    @PreAuthorize("@ss.hasPermi('system:post:query')")
    @GetMapping(value = "/{postId}")
    public AjaxResult getInfo(@PathVariable Long postId) {
        return success(postService.selectPostById(postId));
    }
    /**
     * 新增岗位
     */
    @PreAuthorize("@ss.hasPermi('system:post:add')")
    @Log(title = "岗位管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysPost post) {
        if (!postService.checkPostNameUnique(post)) {
            return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
        } else if (!postService.checkPostCodeUnique(post)) {
            return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
        }
        post.setCreateBy(getUsername());
        return toAjax(postService.insertPost(post));
    }
    /**
     * 修改岗位
     */
    @PreAuthorize("@ss.hasPermi('system:post:edit')")
    @Log(title = "岗位管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysPost post) {
        if (!postService.checkPostNameUnique(post)) {
            return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
        } else if (!postService.checkPostCodeUnique(post)) {
            return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
        }
        post.setUpdateBy(getUsername());
        return toAjax(postService.updatePost(post));
    }
    /**
     * 删除岗位
     */
    @PreAuthorize("@ss.hasPermi('system:post:remove')")
    @Log(title = "岗位管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{postIds}")
    public AjaxResult remove(@PathVariable Long[] postIds) {
        return toAjax(postService.deletePostByIds(postIds));
    }
    /**
     * 获取岗位选择框列表
     */
    @GetMapping("/optionselect")
    public AjaxResult optionselect() {
        List<SysPost> posts = postService.selectPostAll();
        return success(posts);
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
New file
@@ -0,0 +1,127 @@
package com.ruoyi.web.controller.system;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
/**
 * 个人信息 业务处理
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController {
    @Autowired
    private ISysUserService userService;
    @Autowired
    private TokenService tokenService;
    /**
     * 个人信息
     */
    @GetMapping
    public AjaxResult profile() {
        LoginUser loginUser = getLoginUser();
        SysUser user = loginUser.getUser();
        AjaxResult ajax = AjaxResult.success(user);
        ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
        ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
        return ajax;
    }
    /**
     * 修改用户
     */
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult updateProfile(@RequestBody SysUser user) {
        LoginUser loginUser = getLoginUser();
        SysUser currentUser = loginUser.getUser();
        currentUser.setNickName(user.getNickName());
        currentUser.setEmail(user.getEmail());
        currentUser.setPhonenumber(user.getPhonenumber());
        currentUser.setSex(user.getSex());
        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) {
            return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
        }
        if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) {
            return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
        }
        if (userService.updateUserProfile(currentUser) > 0) {
            // 更新缓存用户信息
            tokenService.setLoginUser(loginUser);
            return success();
        }
        return error("修改个人信息异常,请联系管理员");
    }
    /**
     * 重置密码
     */
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping("/updatePwd")
    public AjaxResult updatePwd(@RequestBody Map<String, String> params) {
        String oldPassword = params.get("oldPassword");
        String newPassword = params.get("newPassword");
        LoginUser loginUser = getLoginUser();
        String userName = loginUser.getUsername();
        String password = loginUser.getPassword();
        if (!SecurityUtils.matchesPassword(oldPassword, password)) {
            return error("修改密码失败,旧密码错误");
        }
        if (SecurityUtils.matchesPassword(newPassword, password)) {
            return error("新密码不能与旧密码相同");
        }
        newPassword = SecurityUtils.encryptPassword(newPassword);
        if (userService.resetUserPwd(userName, newPassword) > 0) {
            // 更新缓存用户密码
            loginUser.getUser().setPassword(newPassword);
            tokenService.setLoginUser(loginUser);
            return success();
        }
        return error("修改密码异常,请联系管理员");
    }
    /**
     * 头像上传
     */
    @Log(title = "用户头像", businessType = BusinessType.UPDATE)
    @PostMapping("/avatar")
    public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception {
        if (!file.isEmpty()) {
            LoginUser loginUser = getLoginUser();
            String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
            if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) {
                AjaxResult ajax = AjaxResult.success();
                ajax.put("imgUrl", avatar);
                // 更新缓存用户头像
                loginUser.getUser().setAvatar(avatar);
                tokenService.setLoginUser(loginUser);
                return ajax;
            }
        }
        return error("上传图片异常,请联系管理员");
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
New file
@@ -0,0 +1,35 @@
package com.ruoyi.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysRegisterService;
import com.ruoyi.system.service.ISysConfigService;
/**
 * 注册验证
 *
 * @author ruoyi
 */
@RestController
public class SysRegisterController extends BaseController {
    @Autowired
    private SysRegisterService registerService;
    @Autowired
    private ISysConfigService configService;
    @PostMapping("/register")
    public AjaxResult register(@RequestBody RegisterBody user) {
        if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) {
            return error("当前系统没有开启注册功能!");
        }
        String msg = registerService.register(user);
        return StringUtils.isEmpty(msg) ? success() : error(msg);
    }
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
New file
@@ -0,0 +1,314 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.sys.EditCourierDTO;
import com.ruoyi.system.object.dto.AddSysRoleDTO;
import com.ruoyi.system.object.dto.EditSysRoleDTO;
import com.ruoyi.system.object.vo.SysDeptPageVO;
import com.ruoyi.system.object.vo.SysRolePageVO;
import com.ruoyi.system.object.vo.SysRoleVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
/**
 * 角色信息
 *
 * @author ruoyi
 */
@Api(value = "角色管理控制器",tags = "角色管理控制器")
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController {
    @Autowired
    private ISysRoleService roleService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private SysPermissionService permissionService;
    @Autowired
    private ISysUserService userService;
    @Autowired
    private ISysDeptService deptService;
/*
    @ApiOperation(value = "角色列表", notes = "角色列表")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysRole role) {
        startPage();
        List<SysRole> list = roleService.selectRoleList(role);
        return getDataTable(list);
    }
*/
    @GetMapping("/page")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @ApiOperation(value = "角色管理-分页列表", tags = "系统后台-权限管理")
    public R<IPage<SysRolePageVO>> page(@RequestParam(value = "pageNum",defaultValue = "0")Integer pageNum,
                                        @RequestParam(value = "pageSize",defaultValue="10")Integer pageSize,
                                        @RequestParam(value = "name",required = false)String name) {
        IPage<SysRolePageVO> iPage = new Page<>(pageNum, pageSize);
        return R.ok(roleService.page(iPage,name));
    }
/*
    @Log(title = "角色管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:role:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysRole role) {
        List<SysRole> list = roleService.selectRoleList(role);
        ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
        util.exportExcel(response, list, "角色数据");
    }
*/
    /**
     * 根据角色编号获取详细信息
     */
/*
    @ApiOperation(value = "根据角色编号获取详细信息", notes = "根据角色编号获取详细信息")
    @PreAuthorize("@ss.hasPermi('system:role:query')")
    @GetMapping(value = "/{roleId}")
    public AjaxResult getInfo(@PathVariable Long roleId) {
        roleService.checkRoleDataScope(roleId);
        return success(roleService.selectRoleById(roleId));
    }
*/
    /**
     * 新增角色
     */
/*    @ApiOperation(value = "新增角色", notes = "新增角色")
    @PreAuthorize("@ss.hasPermi('system:role:add')")
    @Log(title = "角色管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysRole role) {
        role.setRoleKey(RandomUtil.randomString(20));
        if (!roleService.checkRoleNameUnique(role)) {
            return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
        } else if (!roleService.checkRoleKeyUnique(role)) {
            return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
        }
        role.setCreateBy(getUsername());
        return toAjax(roleService.insertRole(role));
    }*/
    @ApiOperation(value = "新增角色", notes = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @Log(title = "角色管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R<Void> add(@Valid @RequestBody AddSysRoleDTO dto) {
        roleService.add(dto);
        return R.ok();
    }
    /**
     * 修改保存角色
     */
    @ApiOperation(value = "修改角色", notes = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @Log(title = "角色管理", businessType = BusinessType.INSERT)
    @PutMapping("/edit")
    public R<Void> edit(@Valid @RequestBody EditSysRoleDTO dto) {
        roleService.edit(dto);
        // 更新缓存用户权限
        LoginUser loginUser = getLoginUser();
        if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) {
            loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
            loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
            tokenService.setLoginUser(loginUser);
        }
        return R.ok();
    }
   /* @ApiOperation(value = "修改保存角色", notes = "修改保存角色")
    @PreAuthorize("@ss.hasPermi('system:role:edit')")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysRole role) {
        role.setRoleKey(RandomUtil.randomString(20));
        roleService.checkRoleAllowed(role);
        roleService.checkRoleDataScope(role.getRoleId());
        if (!roleService.checkRoleNameUnique(role)) {
            return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
        } else if (!roleService.checkRoleKeyUnique(role)) {
            return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
        }
        role.setUpdateBy(getUsername());
        if (roleService.updateRole(role) > 0) {
            // 更新缓存用户权限
            LoginUser loginUser = getLoginUser();
            if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) {
                loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
                loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
                tokenService.setLoginUser(loginUser);
            }
            return success();
        }
        return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
    }*/
    /**
     * 修改保存数据权限
     */
 /*   @PreAuthorize("@ss.hasPermi('system:role:edit')")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping("/dataScope")
    public AjaxResult dataScope(@RequestBody SysRole role) {
        roleService.checkRoleAllowed(role);
        roleService.checkRoleDataScope(role.getRoleId());
        return toAjax(roleService.authDataScope(role));
    }
*/
    /**
     * 状态修改
     */
/*    @PreAuthorize("@ss.hasPermi('system:role:edit')")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysRole role) {
        roleService.checkRoleAllowed(role);
        roleService.checkRoleDataScope(role.getRoleId());
        role.setUpdateBy(getUsername());
        return toAjax(roleService.updateRoleStatus(role));
    }*/
    /**
     * 删除角色
     */
    @ApiOperation(value = "删除角色", notes = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @Log(title = "角色管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{roleId}")
    public R<Void> remove(@PathVariable Long roleId) {
        roleService.deleteRoleById(roleId);
        return R.ok();
    }
    /**
     * 查看详情
     */
    @ApiOperation(value = "查看详情", notes = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @Log(title = "角色管理", businessType = BusinessType.DELETE)
    @GetMapping("/{roleId}")
    public R<SysRoleVO> getById(@PathVariable Long roleId) {
        return R.ok(roleService.getById(roleId));
    }
    /**
     * 获取角色选择框列表
     */
/*
    @ApiOperation(value = "获取角色选择框列表", notes = "获取角色选择框列表")
    @PreAuthorize("@ss.hasPermi('system:role:query')")
    @GetMapping("/optionselect")
    public AjaxResult optionselect() {
        return success(roleService.selectRoleAll());
    }
*/
    /**
     * 查询已分配用户角色列表
     */
/*    @ApiOperation(value = "查询已分配用户角色列表", notes = "查询已分配用户角色列表")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @GetMapping("/authUser/allocatedList")
    public TableDataInfo allocatedList(SysUser user) {
        startPage();
        List<SysUser> list = userService.selectAllocatedList(user);
        return getDataTable(list);
    }*/
    /**
     * 查询未分配用户角色列表
     */
/*    @ApiOperation(value = "查询未分配用户角色列表", notes = "查询未分配用户角色列表")
    @PreAuthorize("@ss.hasPermi('system:role:list')")
    @GetMapping("/authUser/unallocatedList")
    public TableDataInfo unallocatedList(SysUser user) {
        startPage();
        List<SysUser> list = userService.selectUnallocatedList(user);
        return getDataTable(list);
    }*/
    /**
     * 取消授权用户
     */
/*    @ApiOperation(value = "取消授权用户", notes = "取消授权用户")
    @PreAuthorize("@ss.hasPermi('system:role:edit')")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/cancel")
    public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) {
        return toAjax(roleService.deleteAuthUser(userRole));
    }*/
    /**
     * 批量取消授权用户
     */
/*    @ApiOperation(value = "批量取消授权用户", notes = "批量取消授权用户")
    @PreAuthorize("@ss.hasPermi('system:role:edit')")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/cancelAll")
    public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) {
        return toAjax(roleService.deleteAuthUsers(roleId, userIds));
    }*/
    /**
     * 批量选择用户授权
     */
/*    @ApiOperation(value = "批量选择用户授权", notes = "批量选择用户授权")
    @PreAuthorize("@ss.hasPermi('system:role:edit')")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/selectAll")
    public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) {
        roleService.checkRoleDataScope(roleId);
        return toAjax(roleService.insertAuthUsers(roleId, userIds));
    }*/
    /**
     * 获取对应角色部门树列表
     */
/*    @PreAuthorize("@ss.hasPermi('system:role:query')")
    @GetMapping(value = "/deptTree/{roleId}")
    public AjaxResult deptTree(@PathVariable("roleId") Long roleId) {
        AjaxResult ajax = AjaxResult.success();
        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
        ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
        return ajax;
    }*/
}
pt-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
New file
@@ -0,0 +1,238 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.errand.object.dto.sys.OrderPageListDTO;
import com.ruoyi.errand.object.vo.sys.OrderPageListVO;
import com.ruoyi.system.object.dto.AddSysUserDTO;
import com.ruoyi.system.object.vo.EditSysUserDTO;
import com.ruoyi.system.object.vo.SysUserPageListVO;
import com.ruoyi.system.object.vo.SysUserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
/**
 * 用户信息
 *
 * @author ruoyi
 */
@Api(value = "账号管理控制器",tags = "账号管理控制器")
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
    @Autowired
    private ISysUserService userService;
    @Autowired
    private ISysRoleService roleService;
    @Autowired
    private ISysDeptService deptService;
    @Autowired
    private ISysPostService postService;
    /**
     * 获取用户列表
     */
    @ApiOperation(value = "账号管理-获取账号分页列表", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/list")
    public R<IPage<SysUserPageListVO>> getSysUserPageList(@RequestParam("pageNum")Integer pageNum,
                                                          @RequestParam("pageSize")Integer pageSize,
                                                          @RequestParam("nickName")String nickName,
                                                          @RequestParam("phone")Integer phone,
                                                          @RequestParam("status")String status) {
        IPage<SysUserPageListVO> page=new Page<>(pageNum,pageSize);
        return R.ok(userService.getSysUserPageList(page,nickName,phone,status));
    }
/*
    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:user:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysUser user) {
        List<SysUser> list = userService.selectUserList(user);
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        util.exportExcel(response, list, "用户数据");
    }*/
 /*   @Log(title = "用户管理", businessType = BusinessType.IMPORT)
    @PreAuthorize("@ss.hasPermi('system:user:import')")
    @PostMapping("/importData")
    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        List<SysUser> userList = util.importExcel(file.getInputStream());
        String operName = getUsername();
        String message = userService.importUser(userList, updateSupport, operName);
        return success(message);
    }
*/
/*    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        util.importTemplateExcel(response, "用户数据");
    }*/
    /**
     * 根据用户编号回显
     */
    @ApiOperation(value = "账号管理-查看详情(回显)", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping(value ="/{userId}")
    public R<SysUserVO> getInfo(@PathVariable(value = "userId") Long userId) {
        return userService.getInfo(userId);
    }
    /**
     * 新增用户
     */
    @ApiOperation(value = "账号管理-查看详情(回显)", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @Log(title = "用户管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R<Void> add(@Valid @RequestBody AddSysUserDTO dto) {
        userService.add(dto);
        return R.ok();
    }
    /**
     * 修改用户
     */
    @ApiOperation(value = "账号管理-修改用户", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PostMapping("/edit")
    public R<Void> edit(@Valid @RequestBody EditSysUserDTO dto) {
        userService.edit(dto);
        return R.ok();
    }
   /* @ApiOperation(value = "修改用户", notes = "修改用户")
    @PreAuthorize("@ss.hasPermi('system:user:edit')")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysUser user) {
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getUserId());
        deptService.checkDeptDataScope(user.getDeptId());
        roleService.checkRoleDataScope(user.getRoleIds());
        if (!userService.checkUserNameUnique(user)) {
            return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
            return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
            return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        user.setUpdateBy(getUsername());
        return toAjax(userService.updateUser(user));
    }*/
    /**
     * 删除用户
     */
    @ApiOperation(value = "账号管理-删除用户", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @Log(title = "用户管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{userId}")
    public R remove(@PathVariable("userId") Long userId) {
        if (Objects.equals(userId, getUserId())) {
            return R.fail("当前用户不能删除");
        }
        return R.ok(userService.deleteUserById(userId));
    }
    /**
     * 重置密码
     */
    @ApiOperation(value = "账号管理-重置密码", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/resetPassword/{userId}")
    public R<Void> resetPassword(@PathVariable("userId") Long userId) {
        userService.resetPassword(userId);
        return R.ok();
    }
    /**
     * 状态修改
     */
    @ApiOperation(value = "账号管理-状态修改", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus/{userId}")
    public R<Void> changeStatus(@PathVariable("userId") Long userId) {
        if (Objects.equals(userId, getUserId())) {
            return R.fail("当前用户不能删除");
        }
        userService.changeStatus(userId);
        return R.ok();
    }
    /**
     * 根据用户编号获取授权角色
     */
/*    @ApiOperation(value = "根据用户编号获取授权角色", notes = "根据用户编号获取授权角色")
    @PreAuthorize("@ss.hasPermi('system:user:query')")
    @GetMapping("/authRole/{userId}")
    public AjaxResult authRole(@PathVariable("userId") Long userId) {
        AjaxResult ajax = AjaxResult.success();
        SysUser user = userService.selectUserById(userId);
        List<SysRole> roles = roleService.selectRolesByUserId(userId);
        ajax.put("user", user);
        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
        return ajax;
    }*/
    /**
     * 用户授权角色
     */
/*    @ApiOperation(value = "用户授权角色", notes = "用户授权角色")
    @PreAuthorize("@ss.hasPermi('system:user:edit')")
    @Log(title = "用户管理", businessType = BusinessType.GRANT)
    @PutMapping("/authRole")
    public AjaxResult insertAuthRole(Long userId, Long[] roleIds) {
        userService.checkUserDataScope(userId);
        roleService.checkRoleDataScope(roleIds);
        userService.insertUserAuth(userId, roleIds);
        return success();
    }*/
    /**
     * 获取部门树列表
     */
/*    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/deptTree")
    public AjaxResult deptTree(SysDept dept) {
        return success(deptService.selectDeptTreeList(dept));
    }*/
}
pt-admin/src/main/java/com/ruoyi/web/controller/task/MessageTask.java
New file
@@ -0,0 +1,11 @@
package com.ruoyi.web.controller.task;
import org.springframework.stereotype.Component;
@Component
public class MessageTask {
}
pt-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java
New file
@@ -0,0 +1,125 @@
package com.ruoyi.web.core.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
/**
 * Swagger2的接口配置
 *
 * @author ruoyi
 */
@Configuration
public class SwaggerConfig
{
    /** 系统基础配置 */
    @Autowired
    private RuoYiConfig ruoyiConfig;
    /** 是否开启swagger */
    @Value("${swagger.enabled}")
    private boolean enabled;
    /** 设置请求的统一前缀 */
    @Value("${swagger.pathMapping}")
    private String pathMapping;
    /**
     * 创建API
     */
    @Bean
    public Docket createRestApi()
    {
        return new Docket(DocumentationType.OAS_30)
                // 是否启用Swagger
                .enable(enabled)
                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
                .apiInfo(apiInfo())
                // 设置哪些接口暴露给Swagger展示
                .select()
                // 扫描所有有注解的api,用这种方式更灵活
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                // 扫描指定包中的swagger注解
                // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                /* 设置安全模式,swagger可以设置访问token */
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts())
                .pathMapping(pathMapping);
    }
    /**
     * 安全模式,这里指定token通过Authorization头请求头传递
     */
    private List<SecurityScheme> securitySchemes()
    {
        List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
        apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
        return apiKeyList;
    }
    /**
     * 安全上下文
     */
    private List<SecurityContext> securityContexts()
    {
        List<SecurityContext> securityContexts = new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
                        .build());
        return securityContexts;
    }
    /**
     * 默认的安全上引用
     */
    private List<SecurityReference> defaultAuth()
    {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
        return securityReferences;
    }
    /**
     * 添加摘要信息
     */
    private ApiInfo apiInfo()
    {
        // 用ApiInfoBuilder进行定制
        return new ApiInfoBuilder()
                // 设置标题
                .title("标题:昆明跑腿_接口文档")
                // 描述
                .description("")
                // 作者信息
                .contact(new Contact(ruoyiConfig.getName(), null, null))
                // 版本
                .version("版本号:" + ruoyiConfig.getVersion())
                .build();
    }
}
pt-admin/src/main/resources/META-INF/spring-devtools.properties
New file
@@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar
pt-admin/src/main/resources/application-druid.yml
New file
@@ -0,0 +1,61 @@
# 数据源配置
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        druid:
            # 主库数据源
            master:
                url: jdbc:mysql://127.0.0.1:3306/paotui?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            # 从库数据源
            slave:
                # 从数据源开关/默认关闭
                enabled: false
                url:
                username:
                password:
            # 初始连接数
            initialSize: 5
            # 最小连接池数量
            minIdle: 10
            # 最大连接池数量
            maxActive: 20
            # 配置获取连接等待超时的时间
            maxWait: 60000
            # 配置连接超时时间
            connectTimeout: 30000
            # 配置网络超时时间
            socketTimeout: 60000
            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
            timeBetweenEvictionRunsMillis: 60000
            # 配置一个连接在池中最小生存的时间,单位是毫秒
            minEvictableIdleTimeMillis: 300000
            # 配置一个连接在池中最大生存的时间,单位是毫秒
            maxEvictableIdleTimeMillis: 900000
            # 配置检测连接是否有效
            validationQuery: SELECT 1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: true
                # 设置白名单,不填则允许所有访问
                allow:
                url-pattern: /druid/*
                # 控制台管理用户名和密码
                login-username: ruoyi
                login-password: 123456
            filter:
                stat:
                    enabled: true
                    # 慢SQL记录
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true
pt-admin/src/main/resources/application.yml
New file
@@ -0,0 +1,209 @@
# 项目相关配置
ruoyi:
  # 名称
  name: paotui
  # 版本
  version: 3.8.9
  # 版权年份
  copyrightYear: 2025
  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: D:/paotuiApp/javaProject/pic
  # 获取ip地址开关
  addressEnabled: false
  # 验证码类型 math 数字计算 char 字符验证
  captchaType: math
# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8081
  servlet:
    # 应用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # 连接数满后的排队数,默认为100
    accept-count: 1000
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100
# 日志配置
logging:
  level:
    com.ruoyi: debug
    org.springframework: warn
# 用户配置
user:
  password:
    # 密码最大错误次数
    maxRetryCount: 5
    # 密码锁定时间(默认10分钟)
    lockTime: 10
# Spring配置
spring:
  # 资源信息
  messages:
    # 国际化资源文件路径
    basename: i18n/messages
  profiles:
    active: druid
  # 文件上传
  servlet:
    multipart:
      # 单个文件大小
      max-file-size: 10MB
      # 设置总上传的文件大小
      max-request-size: 20MB
  # 服务模块
  devtools:
    restart:
      # 热部署开关
      enabled: true
  # redis 配置
  redis:
    # 地址
    host: 127.0.0.1
    # 端口,默认为6379
    port: 6379
    # 数据库索引
    database: 0
    # 密码
    password: 123456
    # 连接超时时间
    timeout: 10s
    jedis:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池的最大数据库连接数
        max-active: 8
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        time-between-eviction-runs:
# token配置
token:
  # 令牌自定义标识
  header: Authorization
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认1440分钟,24小时)
  expireTime: 1440
## MyBatis配置
#mybatis:
#  # 搜索指定包别名
#  typeAliasesPackage: com.ruoyi.**.domain
#  # 配置mapper的扫描,找到所有的mapper.xml映射文件
#  mapperLocations: classpath*:mapper/**/*Mapper.xml
#  # 加载全局的配置文件
#  configLocation: classpath:mybatis/mybatis-config.xml
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  typeAliasesPackage: com.ruoyi.**.domain
  #  config-location:  classpath:mybatis/mybatis-config.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    cache-enabled: true
    use-generated-keys: true
    default-executor-type: simple
# PageHelper分页插件
pagehelper:
  helperDialect: mysql
  supportMethodsArguments: true
  params: count=countSql
# Swagger配置
swagger:
  # 是否开启swagger
  enabled: true
  # 请求前缀
  pathMapping: /
# 防止XSS攻击
xss:
  # 过滤开关
  enabled: true
  # 排除链接(多个用逗号分隔)
  excludes: /system/notice
  # 匹配链接
  urlPatterns: /system/*,/monitor/*,/tool/*
wx:
  appletsAppid: wxdeed472c98e42a54
  appletsAppSecret: c89c697c981452480e0781fb82d4284c
  # 不校验白名单
weapp:
  security:
    exclude-urls:
      - /ws/**
      - /app/user/getSMSCode
      - /app/user/mobileLogin
      - /app/user/appletLogin
      - /app/user/register
      - /app/region/getProvinceList1
      - /app/community/getCommunity
      - /app/agreement/getAgreementByType
      - /app/agreement/addAgreement
      - /app/systemConfig/index/start
      - /app/systemConfig/home
      - /app/banner/getBannerList
      - /app/vipSetting/getVipInfoList
      - /app/order/orderPaymentCallback
      - /app/order/refundPayMoneyCallback
      - /app/vipOrder/orderPaymentCallback
      - /app/user/statistics
      - /app/user/userTopInfo
      - /app/community/getTotalCommunityList
      - /app/order/orderTopInfo
      - /app/order/statistics
      - /app/order/financeStatistics
      - /app/order/financeStatistics/export
      - /app/order/list
      - /app/order/list/export
      - /app/order/detail
      - /app/user/list
      - /app/user/detail
      - /app/user/froze
      - /app/user/refund
      - /app/courier/getAllCourierList
      - /app/courier/list
      - /app/courier/detail
      - /app/courier/add
      - /app/courier/edit
      - /app/courier/delete
      - /app/courier/froze
      - /app/community/getTotalCommunityList
      - /app/community/getAllCommunityList
      - /app/community/list
      - /app/community/add
      - /app/community/edit
      - /app/community/delete
      - /app/community/froze
      - /app/community/detail
      - /app/report/list
      - /app/report/dispose
      - /app/report/delete
      - /app/systemConfig/startPage/add
      - /app/systemConfig/index/start
      - /app/banner/list
      - /app/banner/add
      - /app/banner/edit
      - /app/banner/delete
      - /app/banner/detail
      - /app/phone/saveServicePhone
      - /app/vipSetting/setPrice
      - /app/vipSetting/getVipList
      - /app/feedback/list
      - /app/feedback/delete
      - /app/feedback/dispose
pt-admin/src/main/resources/banner.txt
New file
@@ -0,0 +1,2 @@
Application Version: ${ruoyi.version}
Spring Boot Version: ${spring-boot.version}
pt-admin/src/main/resources/i18n/messages.properties
New file
@@ -0,0 +1,38 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾,访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
pt-admin/src/main/resources/logback.xml
New file
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 日志存放路径 -->
    <property name="log.path" value="D:/congzhouApp/javaProject/logs" />
    <!-- 日志输出格式 -->
    <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
    <!-- 系统日志输出 -->
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </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.path}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</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>
    <!-- 用户访问日志输出  -->
    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-user.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按天回滚 daily -->
            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
    <!-- 系统模块日志级别控制  -->
    <logger name="com.ruoyi" level="info" />
    <!-- Spring日志级别控制  -->
    <logger name="org.springframework" level="warn" />
    <root level="info">
        <appender-ref ref="console" />
    </root>
    <!--系统操作日志-->
    <root level="info">
        <appender-ref ref="file_info" />
        <appender-ref ref="file_error" />
    </root>
    <!--系统用户操作日志-->
    <logger name="sys-user" level="info">
        <appender-ref ref="sys-user"/>
    </logger>
</configuration>
pt-admin/src/main/resources/mybatis/mybatis-config.xml
New file
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 全局参数 -->
    <settings>
        <!-- 使全局的映射器启用或禁用缓存 -->
        <setting name="cacheEnabled"             value="true"   />
        <!-- 允许JDBC 支持自动生成主键 -->
        <setting name="useGeneratedKeys"         value="true"   />
        <!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
        <setting name="defaultExecutorType"      value="SIMPLE" />
        <!-- 指定 MyBatis 所用日志的具体实现 -->
        <setting name="logImpl"                  value="SLF4J"  />
        <!-- 使用驼峰命名法转换字段 -->
        <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
    </settings>
</configuration>
pt-admin/target/classes/META-INF/spring-devtools.properties
New file
@@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar
pt-admin/target/classes/application-druid.yml
New file
@@ -0,0 +1,61 @@
# 数据源配置
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        druid:
            # 主库数据源
            master:
                url: jdbc:mysql://127.0.0.1:3306/paotui?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            # 从库数据源
            slave:
                # 从数据源开关/默认关闭
                enabled: false
                url:
                username:
                password:
            # 初始连接数
            initialSize: 5
            # 最小连接池数量
            minIdle: 10
            # 最大连接池数量
            maxActive: 20
            # 配置获取连接等待超时的时间
            maxWait: 60000
            # 配置连接超时时间
            connectTimeout: 30000
            # 配置网络超时时间
            socketTimeout: 60000
            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
            timeBetweenEvictionRunsMillis: 60000
            # 配置一个连接在池中最小生存的时间,单位是毫秒
            minEvictableIdleTimeMillis: 300000
            # 配置一个连接在池中最大生存的时间,单位是毫秒
            maxEvictableIdleTimeMillis: 900000
            # 配置检测连接是否有效
            validationQuery: SELECT 1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: true
                # 设置白名单,不填则允许所有访问
                allow:
                url-pattern: /druid/*
                # 控制台管理用户名和密码
                login-username: ruoyi
                login-password: 123456
            filter:
                stat:
                    enabled: true
                    # 慢SQL记录
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true
pt-admin/target/classes/application.yml
New file
@@ -0,0 +1,209 @@
# 项目相关配置
ruoyi:
  # 名称
  name: paotui
  # 版本
  version: 3.8.9
  # 版权年份
  copyrightYear: 2025
  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: D:/paotuiApp/javaProject/pic
  # 获取ip地址开关
  addressEnabled: false
  # 验证码类型 math 数字计算 char 字符验证
  captchaType: math
# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8081
  servlet:
    # 应用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # 连接数满后的排队数,默认为100
    accept-count: 1000
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100
# 日志配置
logging:
  level:
    com.ruoyi: debug
    org.springframework: warn
# 用户配置
user:
  password:
    # 密码最大错误次数
    maxRetryCount: 5
    # 密码锁定时间(默认10分钟)
    lockTime: 10
# Spring配置
spring:
  # 资源信息
  messages:
    # 国际化资源文件路径
    basename: i18n/messages
  profiles:
    active: druid
  # 文件上传
  servlet:
    multipart:
      # 单个文件大小
      max-file-size: 10MB
      # 设置总上传的文件大小
      max-request-size: 20MB
  # 服务模块
  devtools:
    restart:
      # 热部署开关
      enabled: true
  # redis 配置
  redis:
    # 地址
    host: 127.0.0.1
    # 端口,默认为6379
    port: 6379
    # 数据库索引
    database: 0
    # 密码
    password: 123456
    # 连接超时时间
    timeout: 10s
    jedis:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池的最大数据库连接数
        max-active: 8
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        time-between-eviction-runs:
# token配置
token:
  # 令牌自定义标识
  header: Authorization
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认1440分钟,24小时)
  expireTime: 1440
## MyBatis配置
#mybatis:
#  # 搜索指定包别名
#  typeAliasesPackage: com.ruoyi.**.domain
#  # 配置mapper的扫描,找到所有的mapper.xml映射文件
#  mapperLocations: classpath*:mapper/**/*Mapper.xml
#  # 加载全局的配置文件
#  configLocation: classpath:mybatis/mybatis-config.xml
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  typeAliasesPackage: com.ruoyi.**.domain
  #  config-location:  classpath:mybatis/mybatis-config.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    cache-enabled: true
    use-generated-keys: true
    default-executor-type: simple
# PageHelper分页插件
pagehelper:
  helperDialect: mysql
  supportMethodsArguments: true
  params: count=countSql
# Swagger配置
swagger:
  # 是否开启swagger
  enabled: true
  # 请求前缀
  pathMapping: /
# 防止XSS攻击
xss:
  # 过滤开关
  enabled: true
  # 排除链接(多个用逗号分隔)
  excludes: /system/notice
  # 匹配链接
  urlPatterns: /system/*,/monitor/*,/tool/*
wx:
  appletsAppid: wxdeed472c98e42a54
  appletsAppSecret: c89c697c981452480e0781fb82d4284c
  # 不校验白名单
weapp:
  security:
    exclude-urls:
      - /ws/**
      - /app/user/getSMSCode
      - /app/user/mobileLogin
      - /app/user/appletLogin
      - /app/user/register
      - /app/region/getProvinceList1
      - /app/community/getCommunity
      - /app/agreement/getAgreementByType
      - /app/agreement/addAgreement
      - /app/systemConfig/index/start
      - /app/systemConfig/home
      - /app/banner/getBannerList
      - /app/vipSetting/getVipInfoList
      - /app/order/orderPaymentCallback
      - /app/order/refundPayMoneyCallback
      - /app/vipOrder/orderPaymentCallback
      - /app/user/statistics
      - /app/user/userTopInfo
      - /app/community/getTotalCommunityList
      - /app/order/orderTopInfo
      - /app/order/statistics
      - /app/order/financeStatistics
      - /app/order/financeStatistics/export
      - /app/order/list
      - /app/order/list/export
      - /app/order/detail
      - /app/user/list
      - /app/user/detail
      - /app/user/froze
      - /app/user/refund
      - /app/courier/getAllCourierList
      - /app/courier/list
      - /app/courier/detail
      - /app/courier/add
      - /app/courier/edit
      - /app/courier/delete
      - /app/courier/froze
      - /app/community/getTotalCommunityList
      - /app/community/getAllCommunityList
      - /app/community/list
      - /app/community/add
      - /app/community/edit
      - /app/community/delete
      - /app/community/froze
      - /app/community/detail
      - /app/report/list
      - /app/report/dispose
      - /app/report/delete
      - /app/systemConfig/startPage/add
      - /app/systemConfig/index/start
      - /app/banner/list
      - /app/banner/add
      - /app/banner/edit
      - /app/banner/delete
      - /app/banner/detail
      - /app/phone/saveServicePhone
      - /app/vipSetting/setPrice
      - /app/vipSetting/getVipList
      - /app/feedback/list
      - /app/feedback/delete
      - /app/feedback/dispose
pt-admin/target/classes/banner.txt
New file
@@ -0,0 +1,2 @@
Application Version: ${ruoyi.version}
Spring Boot Version: ${spring-boot.version}
pt-admin/target/classes/i18n/messages.properties
New file
@@ -0,0 +1,38 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾,访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
pt-admin/target/classes/logback.xml
New file
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 日志存放路径 -->
    <property name="log.path" value="D:/congzhouApp/javaProject/logs" />
    <!-- 日志输出格式 -->
    <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
    <!-- 系统日志输出 -->
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-info.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </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.path}/sys-error.log</file>
        <!-- 循环政策:基于时间创建日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 -->
            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</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>
    <!-- 用户访问日志输出  -->
    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-user.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按天回滚 daily -->
            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 日志最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>
    <!-- 系统模块日志级别控制  -->
    <logger name="com.ruoyi" level="info" />
    <!-- Spring日志级别控制  -->
    <logger name="org.springframework" level="warn" />
    <root level="info">
        <appender-ref ref="console" />
    </root>
    <!--系统操作日志-->
    <root level="info">
        <appender-ref ref="file_info" />
        <appender-ref ref="file_error" />
    </root>
    <!--系统用户操作日志-->
    <logger name="sys-user" level="info">
        <appender-ref ref="sys-user"/>
    </logger>
</configuration>
pt-admin/target/classes/mybatis/mybatis-config.xml
New file
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 全局参数 -->
    <settings>
        <!-- 使全局的映射器启用或禁用缓存 -->
        <setting name="cacheEnabled"             value="true"   />
        <!-- 允许JDBC 支持自动生成主键 -->
        <setting name="useGeneratedKeys"         value="true"   />
        <!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
        <setting name="defaultExecutorType"      value="SIMPLE" />
        <!-- 指定 MyBatis 所用日志的具体实现 -->
        <setting name="logImpl"                  value="SLF4J"  />
        <!-- 使用驼峰命名法转换字段 -->
        <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
    </settings>
</configuration>
pt-common/pom.xml
New file
@@ -0,0 +1,183 @@
<?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">
    <parent>
        <artifactId>kunming-daiban</artifactId>
        <groupId>com.pt</groupId>
        <version>3.8.9</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>pt-common</artifactId>
    <description>
        common通用工具
    </description>
    <dependencies>
        <!-- Spring框架基本的核心工具 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- SpringWeb模块 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <!-- spring security 安全认证 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- pagehelper 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
        </dependency>
        <!-- 自定义验证注解 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--常用工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- JSON工具类 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <!-- 阿里JSON解析器 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>
        <!-- io常用工具类 -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <!-- excel工具 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>
        <!-- yml解析器 -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
        </dependency>
        <!-- Token生成与解析-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <!-- Jaxb -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
        </dependency>
        <!-- redis 缓存操作 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!-- pool 对象池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!-- 解析客户端操作系统、浏览器等 -->
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
        </dependency>
        <!-- servlet包 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>
        <!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.25</version>
        </dependency>
        <!--easypoi-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>4.0.2</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>4.4.0</version>
        </dependency>
    </dependencies>
</project>
pt-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 匿名访问不鉴权注解
 *
 * @author ruoyi
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}
pt-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 数据权限过滤注解
 *
 * @author ruoyi
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
    /**
     * 部门表的别名
     */
    public String deptAlias() default "";
    /**
     * 用户表的别名
     */
    public String userAlias() default "";
    /**
     * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
     */
    public String permission() default "";
}
pt-common/src/main/java/com/ruoyi/common/annotation/DataSource.java
New file
@@ -0,0 +1,28 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.enums.DataSourceType;
/**
 * 自定义多数据源切换注解
 *
 * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
 *
 * @author ruoyi
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切换数据源名称
     */
    public DataSourceType value() default DataSourceType.MASTER;
}
pt-common/src/main/java/com/ruoyi/common/annotation/Excel.java
New file
@@ -0,0 +1,197 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.ruoyi.common.utils.poi.ExcelHandlerAdapter;
/**
 * 自定义导出Excel数据注解
 *
 * @author ruoyi
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
    /**
     * 导出时在excel中排序
     */
    public int sort() default Integer.MAX_VALUE;
    /**
     * 导出到Excel中的名字.
     */
    public String name() default "";
    /**
     * 日期格式, 如: yyyy-MM-dd
     */
    public String dateFormat() default "";
    /**
     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
     */
    public String dictType() default "";
    /**
     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
     */
    public String readConverterExp() default "";
    /**
     * 分隔符,读取字符串组内容
     */
    public String separator() default ",";
    /**
     * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
     */
    public int scale() default -1;
    /**
     * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
     */
    public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
    /**
     * 导出时在excel中每个列的高度
     */
    public double height() default 14;
    /**
     * 导出时在excel中每个列的宽度
     */
    public double width() default 16;
    /**
     * 文字后缀,如% 90 变成90%
     */
    public String suffix() default "";
    /**
     * 当值为空时,字段的默认值
     */
    public String defaultValue() default "";
    /**
     * 提示信息
     */
    public String prompt() default "";
    /**
     * 是否允许内容换行
     */
    public boolean wrapText() default false;
    /**
     * 设置只能选择不能输入的列内容.
     */
    public String[] combo() default {};
    /**
     * 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
     */
    public boolean comboReadDict() default false;
    /**
     * 是否需要纵向合并单元格,应对需求:含有list集合单元格)
     */
    public boolean needMerge() default false;
    /**
     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
     */
    public boolean isExport() default true;
    /**
     * 另一个类中的属性名称,支持多级获取,以小数点隔开
     */
    public String targetAttr() default "";
    /**
     * 是否自动统计数据,在最后追加一行统计数据总和
     */
    public boolean isStatistics() default false;
    /**
     * 导出类型(0数字 1字符串 2图片)
     */
    public ColumnType cellType() default ColumnType.STRING;
    /**
     * 导出列头背景颜色
     */
    public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
    /**
     * 导出列头字体颜色
     */
    public IndexedColors headerColor() default IndexedColors.WHITE;
    /**
     * 导出单元格背景颜色
     */
    public IndexedColors backgroundColor() default IndexedColors.WHITE;
    /**
     * 导出单元格字体颜色
     */
    public IndexedColors color() default IndexedColors.BLACK;
    /**
     * 导出字段对齐方式
     */
    public HorizontalAlignment align() default HorizontalAlignment.CENTER;
    /**
     * 自定义数据处理器
     */
    public Class<?> handler() default ExcelHandlerAdapter.class;
    /**
     * 自定义数据处理器参数
     */
    public String[] args() default {};
    /**
     * 字段类型(0:导出导入;1:仅导出;2:仅导入)
     */
    Type type() default Type.ALL;
    public enum Type
    {
        ALL(0), EXPORT(1), IMPORT(2);
        private final int value;
        Type(int value)
        {
            this.value = value;
        }
        public int value()
        {
            return this.value;
        }
    }
    public enum ColumnType
    {
        NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
        private final int value;
        ColumnType(int value)
        {
            this.value = value;
        }
        public int value()
        {
            return this.value;
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/annotation/Excels.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * Excel注解集
 *
 * @author ruoyi
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Excels
{
    public Excel[] value();
}
pt-common/src/main/java/com/ruoyi/common/annotation/Log.java
New file
@@ -0,0 +1,51 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.OperatorType;
/**
 * 自定义操作日志记录注解
 *
 * @author ruoyi
 *
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
    /**
     * 模块
     */
    public String title() default "";
    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;
    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;
    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;
    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;
    /**
     * 排除指定的请求参数
     */
    public String[] excludeParamNames() default {};
}
pt-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java
New file
@@ -0,0 +1,40 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.enums.LimitType;
/**
 * 限流注解
 *
 * @author ruoyi
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
    /**
     * 限流key
     */
    public String key() default CacheConstants.RATE_LIMIT_KEY;
    /**
     * 限流时间,单位秒
     */
    public int time() default 60;
    /**
     * 限流次数
     */
    public int count() default 100;
    /**
     * 限流类型
     */
    public LimitType limitType() default LimitType.DEFAULT;
}
pt-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java
New file
@@ -0,0 +1,31 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 自定义注解防止表单重复提交
 *
 * @author ruoyi
 *
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{
    /**
     * 间隔时间(ms),小于此时间视为重复提交
     */
    public int interval() default 5000;
    /**
     * 提示消息
     */
    public String message() default "不允许重复提交,请稍候再试";
}
pt-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.config.serializer.SensitiveJsonSerializer;
import com.ruoyi.common.enums.DesensitizedType;
/**
 * 数据脱敏注解
 *
 * @author ruoyi
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
    DesensitizedType desensitizedType();
}
pt-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java
New file
@@ -0,0 +1,122 @@
package com.ruoyi.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * 读取项目相关配置
 *
 * @author ruoyi
 */
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig
{
    /** 项目名称 */
    private String name;
    /** 版本 */
    private String version;
    /** 版权年份 */
    private String copyrightYear;
    /** 上传路径 */
    private static String profile;
    /** 获取地址开关 */
    private static boolean addressEnabled;
    /** 验证码类型 */
    private static String captchaType;
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public String getVersion()
    {
        return version;
    }
    public void setVersion(String version)
    {
        this.version = version;
    }
    public String getCopyrightYear()
    {
        return copyrightYear;
    }
    public void setCopyrightYear(String copyrightYear)
    {
        this.copyrightYear = copyrightYear;
    }
    public static String getProfile()
    {
        return profile;
    }
    public void setProfile(String profile)
    {
        RuoYiConfig.profile = profile;
    }
    public static boolean isAddressEnabled()
    {
        return addressEnabled;
    }
    public void setAddressEnabled(boolean addressEnabled)
    {
        RuoYiConfig.addressEnabled = addressEnabled;
    }
    public static String getCaptchaType() {
        return captchaType;
    }
    public void setCaptchaType(String captchaType) {
        RuoYiConfig.captchaType = captchaType;
    }
    /**
     * 获取导入上传路径
     */
    public static String getImportPath()
    {
        return getProfile() + "/import";
    }
    /**
     * 获取头像上传路径
     */
    public static String getAvatarPath()
    {
        return getProfile() + "/avatar";
    }
    /**
     * 获取下载路径
     */
    public static String getDownloadPath()
    {
        return getProfile() + "/download/";
    }
    /**
     * 获取上传路径
     */
    public static String getUploadPath()
    {
        return getProfile() + "/upload";
    }
}
pt-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java
New file
@@ -0,0 +1,67 @@
package com.ruoyi.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DesensitizedType;
import com.ruoyi.common.utils.SecurityUtils;
/**
 * 数据脱敏序列化过滤
 *
 * @author ruoyi
 */
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
    private DesensitizedType desensitizedType;
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
    {
        if (desensitization())
        {
            gen.writeString(desensitizedType.desensitizer().apply(value));
        }
        else
        {
            gen.writeString(value);
        }
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
            throws JsonMappingException
    {
        Sensitive annotation = property.getAnnotation(Sensitive.class);
        if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
        {
            this.desensitizedType = annotation.desensitizedType();
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
    /**
     * 是否需要脱敏处理
     */
    private boolean desensitization()
    {
        try
        {
            LoginUser securityUser = SecurityUtils.getLoginUser();
            // 管理员不脱敏
            return !securityUser.getUser().isAdmin();
        }
        catch (Exception e)
        {
            return true;
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
New file
@@ -0,0 +1,55 @@
package com.ruoyi.common.constant;
/**
 * 缓存的key 常量
 *
 * @author ruoyi
 */
public class CacheConstants
{
    /**
     * 缓存有效期,默认720(分钟)
     */
    public final static long EXPIRATION = 120;
    public final static long EXPIRATION_APPLET = 7*24*60*60;
    /**
     * 缓存刷新时间,默认120(分钟)
     */
    public final static long REFRESH_TIME = 120;
    /**
     * 登录用户 redis key
     */
    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
    /**
     * 验证码 redis key
     */
    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
    /**
     * 参数管理 cache key
     */
    public static final String SYS_CONFIG_KEY = "sys_config:";
    /**
     * 字典管理 cache key
     */
    public static final String SYS_DICT_KEY = "sys_dict:";
    /**
     * 防重提交 redis key
     */
    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
    /**
     * 限流 redis key
     */
    public static final String RATE_LIMIT_KEY = "rate_limit:";
    /**
     * 登录账户密码错误次数 redis key
     */
    public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
}
pt-common/src/main/java/com/ruoyi/common/constant/Constants.java
New file
@@ -0,0 +1,173 @@
package com.ruoyi.common.constant;
import java.util.Locale;
import io.jsonwebtoken.Claims;
/**
 * 通用常量信息
 *
 * @author ruoyi
 */
public class Constants
{
    /**
     * UTF-8 字符集
     */
    public static final String UTF8 = "UTF-8";
    /**
     * GBK 字符集
     */
    public static final String GBK = "GBK";
    /**
     * 系统语言
     */
    public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
    /**
     * www主域
     */
    public static final String WWW = "www.";
    /**
     * http请求
     */
    public static final String HTTP = "http://";
    /**
     * https请求
     */
    public static final String HTTPS = "https://";
    /**
     * 通用成功标识
     */
    public static final String SUCCESS = "0";
    /**
     * 通用失败标识
     */
    public static final String FAIL = "1";
    /**
     * 登录成功
     */
    public static final String LOGIN_SUCCESS = "Success";
    /**
     * 注销
     */
    public static final String LOGOUT = "Logout";
    /**
     * 注册
     */
    public static final String REGISTER = "Register";
    /**
     * 登录失败
     */
    public static final String LOGIN_FAIL = "Error";
    /**
     * 所有权限标识
     */
    public static final String ALL_PERMISSION = "*:*:*";
    /**
     * 管理员角色权限标识
     */
    public static final String SUPER_ADMIN = "admin";
    /**
     * 角色权限分隔符
     */
    public static final String ROLE_DELIMETER = ",";
    /**
     * 权限标识分隔符
     */
    public static final String PERMISSION_DELIMETER = ",";
    /**
     * 验证码有效期(分钟)
     */
    public static final Integer CAPTCHA_EXPIRATION = 2;
    /**
     * 令牌
     */
    public static final String TOKEN = "token";
    /**
     * 令牌前缀
     */
    public static final String TOKEN_PREFIX = "Bearer ";
    /**
     * 令牌前缀
     */
    public static final String LOGIN_USER_KEY = "login_user_key";
    /**
     * 用户ID
     */
    public static final String JWT_USERID = "userid";
    /**
     * 用户名称
     */
    public static final String JWT_USERNAME = Claims.SUBJECT;
    /**
     * 用户头像
     */
    public static final String JWT_AVATAR = "avatar";
    /**
     * 创建时间
     */
    public static final String JWT_CREATED = "created";
    /**
     * 用户权限
     */
    public static final String JWT_AUTHORITIES = "authorities";
    /**
     * 资源映射路径 前缀
     */
    public static final String RESOURCE_PREFIX = "/profile";
    /**
     * RMI 远程方法调用
     */
    public static final String LOOKUP_RMI = "rmi:";
    /**
     * LDAP 远程方法调用
     */
    public static final String LOOKUP_LDAP = "ldap:";
    /**
     * LDAPS 远程方法调用
     */
    public static final String LOOKUP_LDAPS = "ldaps:";
    /**
     * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全)
     */
    public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
    /**
     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
     */
    public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
    /**
     * 定时任务违规的字符
     */
    public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
            "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
}
pt-common/src/main/java/com/ruoyi/common/constant/GenConstants.java
New file
@@ -0,0 +1,117 @@
package com.ruoyi.common.constant;
/**
 * 代码生成通用常量
 *
 * @author ruoyi
 */
public class GenConstants
{
    /** 单表(增删改查) */
    public static final String TPL_CRUD = "crud";
    /** 树表(增删改查) */
    public static final String TPL_TREE = "tree";
    /** 主子表(增删改查) */
    public static final String TPL_SUB = "sub";
    /** 树编码字段 */
    public static final String TREE_CODE = "treeCode";
    /** 树父编码字段 */
    public static final String TREE_PARENT_CODE = "treeParentCode";
    /** 树名称字段 */
    public static final String TREE_NAME = "treeName";
    /** 上级菜单ID字段 */
    public static final String PARENT_MENU_ID = "parentMenuId";
    /** 上级菜单名称字段 */
    public static final String PARENT_MENU_NAME = "parentMenuName";
    /** 数据库字符串类型 */
    public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
    /** 数据库文本类型 */
    public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };
    /** 数据库时间类型 */
    public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
    /** 数据库数字类型 */
    public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer",
            "bit", "bigint", "float", "double", "decimal" };
    /** 页面不需要编辑字段 */
    public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" };
    /** 页面不需要显示的列表字段 */
    public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by",
            "update_time" };
    /** 页面不需要查询字段 */
    public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by",
            "update_time", "remark" };
    /** Entity基类字段 */
    public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" };
    /** Tree基类字段 */
    public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" };
    /** 文本框 */
    public static final String HTML_INPUT = "input";
    /** 文本域 */
    public static final String HTML_TEXTAREA = "textarea";
    /** 下拉框 */
    public static final String HTML_SELECT = "select";
    /** 单选框 */
    public static final String HTML_RADIO = "radio";
    /** 复选框 */
    public static final String HTML_CHECKBOX = "checkbox";
    /** 日期控件 */
    public static final String HTML_DATETIME = "datetime";
    /** 图片上传控件 */
    public static final String HTML_IMAGE_UPLOAD = "imageUpload";
    /** 文件上传控件 */
    public static final String HTML_FILE_UPLOAD = "fileUpload";
    /** 富文本控件 */
    public static final String HTML_EDITOR = "editor";
    /** 字符串类型 */
    public static final String TYPE_STRING = "String";
    /** 整型 */
    public static final String TYPE_INTEGER = "Integer";
    /** 长整型 */
    public static final String TYPE_LONG = "Long";
    /** 浮点型 */
    public static final String TYPE_DOUBLE = "Double";
    /** 高精度计算类型 */
    public static final String TYPE_BIGDECIMAL = "BigDecimal";
    /** 时间类型 */
    public static final String TYPE_DATE = "Date";
    /** 模糊查询 */
    public static final String QUERY_LIKE = "LIKE";
    /** 相等查询 */
    public static final String QUERY_EQ = "EQ";
    /** 需要 */
    public static final String REQUIRE = "1";
}
pt-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java
New file
@@ -0,0 +1,94 @@
package com.ruoyi.common.constant;
/**
 * 返回状态码
 *
 * @author ruoyi
 */
public class HttpStatus
{
    /**
     * 操作成功
     */
    public static final int SUCCESS = 200;
    /**
     * 对象创建成功
     */
    public static final int CREATED = 201;
    /**
     * 请求已经被接受
     */
    public static final int ACCEPTED = 202;
    /**
     * 操作已经执行成功,但是没有返回数据
     */
    public static final int NO_CONTENT = 204;
    /**
     * 资源已被移除
     */
    public static final int MOVED_PERM = 301;
    /**
     * 重定向
     */
    public static final int SEE_OTHER = 303;
    /**
     * 资源没有被修改
     */
    public static final int NOT_MODIFIED = 304;
    /**
     * 参数列表错误(缺少,格式不匹配)
     */
    public static final int BAD_REQUEST = 400;
    /**
     * 未授权
     */
    public static final int UNAUTHORIZED = 401;
    /**
     * 访问受限,授权过期
     */
    public static final int FORBIDDEN = 403;
    /**
     * 资源,服务未找到
     */
    public static final int NOT_FOUND = 404;
    /**
     * 不允许的http方法
     */
    public static final int BAD_METHOD = 405;
    /**
     * 资源冲突,或者资源被锁
     */
    public static final int CONFLICT = 409;
    /**
     * 不支持的数据,媒体类型
     */
    public static final int UNSUPPORTED_TYPE = 415;
    /**
     * 系统内部错误
     */
    public static final int ERROR = 500;
    /**
     * 接口未实现
     */
    public static final int NOT_IMPLEMENTED = 501;
    /**
     * 系统警告消息
     */
    public static final int WARN = 601;
}
pt-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java
New file
@@ -0,0 +1,50 @@
package com.ruoyi.common.constant;
/**
 * 任务调度通用常量
 *
 * @author ruoyi
 */
public class ScheduleConstants
{
    public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
    /** 执行目标key */
    public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
    /** 默认 */
    public static final String MISFIRE_DEFAULT = "0";
    /** 立即触发执行 */
    public static final String MISFIRE_IGNORE_MISFIRES = "1";
    /** 触发一次执行 */
    public static final String MISFIRE_FIRE_AND_PROCEED = "2";
    /** 不触发立即执行 */
    public static final String MISFIRE_DO_NOTHING = "3";
    public enum Status
    {
        /**
         * 正常
         */
        NORMAL("0"),
        /**
         * 暂停
         */
        PAUSE("1");
        private String value;
        private Status(String value)
        {
            this.value = value;
        }
        public String getValue()
        {
            return value;
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/constant/SecurityConstants.java
New file
@@ -0,0 +1,63 @@
package com.ruoyi.common.constant;
/**
 * 权限相关通用常量
 *
 * @author ruoyi
 */
public class SecurityConstants
{
    /**
     * 用户ID字段
     */
    public static final String DETAILS_USER_ID = "user_id";
    /**
     * 用户名字段
     */
    public static final String DETAILS_USERNAME = "username";
    /**
     * 授权信息字段
     */
    public static final String AUTHORIZATION_HEADER = "Authorization";
    /**
     * 请求来源
     */
    public static final String FROM_SOURCE = "from-source";
    /**
     * 内部请求
     */
    public static final String INNER = "inner";
    /**
     * 用户标识
     */
    public static final String USER_KEY = "user_key";
    /**
     * 用户类型(system/applet)
     */
    public static final String USER_TYPE = "user_type";
    /**
     * 小程序登录用户标识
     */
    public static final String USER_APPLET_KEY = "user_applet_key";
    /**
     * 登录用户
     */
    public static final String LOGIN_USER = "login_user";
    /**
     * 角色权限
     */
    public static final String ROLE_PERMISSION = "role_permission";
    /**
     * 过期时间
     */
    public static final String EXPIRATION_TIME = "expiration_time";
}
pt-common/src/main/java/com/ruoyi/common/constant/TokenConstants.java
New file
@@ -0,0 +1,35 @@
package com.ruoyi.common.constant;
/**
 * Token的Key常量
 *
 * @author ruoyi
 */
public class TokenConstants
{
    /**
     * 令牌自定义标识
     */
    public static final String AUTHENTICATION = "Authorization";
    /**
     * 令牌前缀
     */
    public static final String PREFIX = "Bearer ";
    /**
     * 令牌秘钥
     */
    public final static String SECRET = "abcdefghijklmnopqrstuvwxyz";
    /**
     * 参数签名
     */
    public static final String SIGN = "sign";
    /**
     * 参数随机字符串
     */
    public static final String NONCE_STR = "nonce_str";
}
pt-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
New file
@@ -0,0 +1,81 @@
package com.ruoyi.common.constant;
/**
 * 用户常量信息
 *
 * @author ruoyi
 */
public class UserConstants
{
    /**
     * 平台内系统用户的唯一标志
     */
    public static final String SYS_USER = "SYS_USER";
    /** 正常状态 */
    public static final String NORMAL = "0";
    /** 异常状态 */
    public static final String EXCEPTION = "1";
    /** 用户封禁状态 */
    public static final String USER_DISABLE = "1";
    /** 角色正常状态 */
    public static final String ROLE_NORMAL = "0";
    /** 角色封禁状态 */
    public static final String ROLE_DISABLE = "1";
    /** 部门正常状态 */
    public static final String DEPT_NORMAL = "0";
    /** 部门停用状态 */
    public static final String DEPT_DISABLE = "1";
    /** 字典正常状态 */
    public static final String DICT_NORMAL = "0";
    /** 是否为系统默认(是) */
    public static final String YES = "Y";
    /** 是否菜单外链(是) */
    public static final String YES_FRAME = "0";
    /** 是否菜单外链(否) */
    public static final String NO_FRAME = "1";
    /** 菜单类型(目录) */
    public static final String TYPE_DIR = "M";
    /** 菜单类型(菜单) */
    public static final String TYPE_MENU = "C";
    /** 菜单类型(按钮) */
    public static final String TYPE_BUTTON = "F";
    /** Layout组件标识 */
    public final static String LAYOUT = "Layout";
    /** ParentView组件标识 */
    public final static String PARENT_VIEW = "ParentView";
    /** InnerLink组件标识 */
    public final static String INNER_LINK = "InnerLink";
    /** 校验是否唯一的返回标识 */
    public final static boolean UNIQUE = true;
    public final static boolean NOT_UNIQUE = false;
    /**
     * 用户名长度限制
     */
    public static final int USERNAME_MIN_LENGTH = 2;
    public static final int USERNAME_MAX_LENGTH = 20;
    /**
     * 密码长度限制
     */
    public static final int PASSWORD_MIN_LENGTH = 5;
    public static final int PASSWORD_MAX_LENGTH = 32;
}
pt-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
New file
@@ -0,0 +1,202 @@
package com.ruoyi.common.core.controller;
import java.beans.PropertyEditorSupport;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
/**
 * web层通用数据处理
 *
 * @author ruoyi
 */
public class BaseController
{
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 将前台传递过来的日期格式的字符串,自动转化为Date类型
     */
    @InitBinder
    public void initBinder(WebDataBinder binder)
    {
        // Date 类型转换
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
        {
            @Override
            public void setAsText(String text)
            {
                setValue(DateUtils.parseDate(text));
            }
        });
    }
    /**
     * 设置请求分页数据
     */
    protected void startPage()
    {
        PageUtils.startPage();
    }
    /**
     * 设置请求排序数据
     */
    protected void startOrderBy()
    {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        if (StringUtils.isNotEmpty(pageDomain.getOrderBy()))
        {
            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
            PageHelper.orderBy(orderBy);
        }
    }
    /**
     * 清理分页的线程变量
     */
    protected void clearPage()
    {
        PageUtils.clearPage();
    }
    /**
     * 响应请求分页数据
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected TableDataInfo getDataTable(List<?> list)
    {
        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(HttpStatus.SUCCESS);
        rspData.setMsg("查询成功");
        rspData.setRows(list);
        rspData.setTotal(new PageInfo(list).getTotal());
        return rspData;
    }
    /**
     * 返回成功
     */
    public AjaxResult success()
    {
        return AjaxResult.success();
    }
    /**
     * 返回失败消息
     */
    public AjaxResult error()
    {
        return AjaxResult.error();
    }
    /**
     * 返回成功消息
     */
    public AjaxResult success(String message)
    {
        return AjaxResult.success(message);
    }
    /**
     * 返回成功消息
     */
    public AjaxResult success(Object data)
    {
        return AjaxResult.success(data);
    }
    /**
     * 返回失败消息
     */
    public AjaxResult error(String message)
    {
        return AjaxResult.error(message);
    }
    /**
     * 返回警告消息
     */
    public AjaxResult warn(String message)
    {
        return AjaxResult.warn(message);
    }
    /**
     * 响应返回结果
     *
     * @param rows 影响行数
     * @return 操作结果
     */
    protected AjaxResult toAjax(int rows)
    {
        return rows > 0 ? AjaxResult.success() : AjaxResult.error();
    }
    /**
     * 响应返回结果
     *
     * @param result 结果
     * @return 操作结果
     */
    protected AjaxResult toAjax(boolean result)
    {
        return result ? success() : error();
    }
    /**
     * 页面跳转
     */
    public String redirect(String url)
    {
        return StringUtils.format("redirect:{}", url);
    }
    /**
     * 获取用户缓存信息
     */
    public LoginUser getLoginUser()
    {
        return SecurityUtils.getLoginUser();
    }
    /**
     * 获取登录用户id
     */
    public Long getUserId()
    {
        return getLoginUser().getUserId();
    }
    /**
     * 获取登录部门id
     */
    public Long getDeptId()
    {
        return getLoginUser().getDeptId();
    }
    /**
     * 获取登录用户名
     */
    public String getUsername()
    {
        return getLoginUser().getUsername();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java
New file
@@ -0,0 +1,204 @@
package com.ruoyi.common.core.domain;
import java.util.HashMap;
import java.util.Objects;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils;
/**
 * 操作消息提醒
 *
 * @author ruoyi
 */
public class AjaxResult extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;
    /**
     * 状态码
     */
    public static final String CODE_TAG = "code";
    /**
     * 返回内容
     */
    public static final String MSG_TAG = "msg";
    /**
     * 数据对象
     */
    public static final String DATA_TAG = "data";
    /**
     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
     */
    public AjaxResult() {
    }
    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg  返回内容
     */
    public AjaxResult(int code, String msg) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }
    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg  返回内容
     * @param data 数据对象
     */
    public AjaxResult(int code, String msg, Object data) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data)) {
            super.put(DATA_TAG, data);
        }
    }
    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success() {
        return AjaxResult.success("操作成功");
    }
    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data) {
        return AjaxResult.success("操作成功", data);
    }
    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @return 成功消息
     */
    public static AjaxResult success(String msg) {
        return AjaxResult.success(msg, null);
    }
    /**
     * 返回成功消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data) {
        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
    }
    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult warn(String msg) {
        return AjaxResult.warn(msg, null);
    }
    /**
     * 返回警告消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult warn(String msg, Object data) {
        return new AjaxResult(HttpStatus.WARN, msg, data);
    }
    /**
     * 返回错误消息
     *
     * @return 错误消息
     */
    public static AjaxResult error() {
        return AjaxResult.error("操作失败");
    }
    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @return 错误消息
     */
    public static AjaxResult error(String msg) {
        return AjaxResult.error(msg, null);
    }
    /**
     * 返回错误消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 错误消息
     */
    public static AjaxResult error(String msg, Object data) {
        return new AjaxResult(HttpStatus.ERROR, msg, data);
    }
    /**
     * 返回错误消息
     *
     * @param code 状态码
     * @param msg  返回内容
     * @return 错误消息
     */
    public static AjaxResult error(int code, String msg) {
        return new AjaxResult(code, msg, null);
    }
    /**
     * 是否为成功消息
     *
     * @return 结果
     */
    public boolean isSuccess() {
        return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
    }
    /**
     * 是否为警告消息
     *
     * @return 结果
     */
    public boolean isWarn() {
        return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG));
    }
    /**
     * 是否为错误消息
     *
     * @return 结果
     */
    public boolean isError() {
        return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG));
    }
    /**
     * 方便链式调用
     *
     * @param key   键
     * @param value 值
     * @return 数据对象
     */
    @Override
    public AjaxResult put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
New file
@@ -0,0 +1,124 @@
package com.ruoyi.common.core.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
/**
 * Entity基类
 *
 * @author ruoyi
 */
public class BaseEntity implements Serializable
{
    private static final long serialVersionUID = 1L;
    /** 搜索值 */
    @JsonIgnore
    @TableField(exist = false)
    private String searchValue;
    /** 创建者 */
    private String createBy;
    @ApiModelProperty(value = "创建时间")
    /** 创建时间 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /** 更新者 */
    private String updateBy;
    /** 更新时间 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /** 备注 */
    private String remark;
    /** 请求参数 */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @TableField(exist = false)
    private Map<String, Object> params;
    public String getSearchValue()
    {
        return searchValue;
    }
    public void setSearchValue(String searchValue)
    {
        this.searchValue = searchValue;
    }
    public String getCreateBy()
    {
        return createBy;
    }
    public void setCreateBy(String createBy)
    {
        this.createBy = createBy;
    }
    public Date getCreateTime()
    {
        return createTime;
    }
    public void setCreateTime(Date createTime)
    {
        this.createTime = createTime;
    }
    public String getUpdateBy()
    {
        return updateBy;
    }
    public void setUpdateBy(String updateBy)
    {
        this.updateBy = updateBy;
    }
    public Date getUpdateTime()
    {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime)
    {
        this.updateTime = updateTime;
    }
    public String getRemark()
    {
        return remark;
    }
    public void setRemark(String remark)
    {
        this.remark = remark;
    }
    public Map<String, Object> getParams()
    {
        if (params == null)
        {
            params = new HashMap<>();
        }
        return params;
    }
    public void setParams(Map<String, Object> params)
    {
        this.params = params;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/BaseResult.java
New file
@@ -0,0 +1,48 @@
package com.ruoyi.common.core.domain;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
/**
 * 业务使用
 * @param <T>
 */
public class BaseResult<T> {
    private Integer code;
    private String msg;
    private T data;
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    @Override
    public String toString() {
        return JSON.toJSONString(this, JSONWriter.Feature.IgnoreNonFieldGetter);
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/R.java
New file
@@ -0,0 +1,115 @@
package com.ruoyi.common.core.domain;
import java.io.Serializable;
import com.ruoyi.common.constant.HttpStatus;
/**
 * 响应信息主体
 *
 * @author ruoyi
 */
public class R<T> implements Serializable
{
    private static final long serialVersionUID = 1L;
    /** 成功 */
    public static final int SUCCESS = HttpStatus.SUCCESS;
    /** 失败 */
    public static final int FAIL = HttpStatus.ERROR;
    private int code;
    private String msg;
    private T data;
    public static <T> R<T> ok()
    {
        return restResult(null, SUCCESS, "操作成功");
    }
    public static <T> R<T> ok(T data)
    {
        return restResult(data, SUCCESS, "操作成功");
    }
    public static <T> R<T> ok(T data, String msg)
    {
        return restResult(data, SUCCESS, msg);
    }
    public static <T> R<T> fail()
    {
        return restResult(null, FAIL, "操作失败");
    }
    public static <T> R<T> fail(String msg)
    {
        return restResult(null, FAIL, msg);
    }
    public static <T> R<T> fail(T data)
    {
        return restResult(data, FAIL, "操作失败");
    }
    public static <T> R<T> fail(T data, String msg)
    {
        return restResult(data, FAIL, msg);
    }
    public static <T> R<T> fail(int code, String msg)
    {
        return restResult(null, code, msg);
    }
    private static <T> R<T> restResult(T data, int code, String msg)
    {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }
    public int getCode()
    {
        return code;
    }
    public void setCode(int code)
    {
        this.code = code;
    }
    public String getMsg()
    {
        return msg;
    }
    public void setMsg(String msg)
    {
        this.msg = msg;
    }
    public T getData()
    {
        return data;
    }
    public void setData(T data)
    {
        this.data = data;
    }
    public static <T> Boolean isError(R<T> ret)
    {
        return !isSuccess(ret);
    }
    public static <T> Boolean isSuccess(R<T> ret)
    {
        return R.SUCCESS == ret.getCode();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/ResponseUtils.java
New file
@@ -0,0 +1,77 @@
package com.ruoyi.common.core.domain;
import com.alibaba.fastjson2.JSONObject;
/**
 * 返回工具
 */
public class ResponseUtils {
    public static <T> BaseResult<T> successResponse(T obj) {
        BaseResult<T> response = new BaseResult<>();
        response.setData(obj);
        response.setCode(200);
        response.setMsg("成功");
        return response;
    }
    public static <T> BaseResult<T> successResponse(String msg) {
        BaseResult<T> response = new BaseResult<>();
        response.setData(null);
        response.setCode(200);
        response.setMsg(msg);
        return response;
    }
    public static <T> BaseResult<T> successResponse(T obj, String msg) {
        BaseResult<T> response = new BaseResult<>();
        response.setData(obj);
        response.setCode(200);
        response.setMsg(msg);
        return response;
    }
    public static BaseResult<Object> successResponse() {
        BaseResult<Object> response = new BaseResult<>();
        response.setCode(200);
        response.setMsg("成功");
        response.setData(new JSONObject());
        return response;
    }
    public static <T> BaseResult<T> errorResponse(T obj, String msg) {
        BaseResult<T> response = new BaseResult<>();
        response.setData(obj);
        response.setCode(500);
        response.setMsg(msg);
        return response;
    }
    public static <T> BaseResult<T> errorResponse(Integer code,String msg) {
        BaseResult<T> response = new BaseResult<>();
        response.setData(null);
        response.setCode(code);
        response.setMsg(msg);
        return response;
    }
    public static <T> BaseResult<T> errorResponse(String msg) {
        BaseResult<T> response = new BaseResult<>();
        response.setData(null);
        response.setCode(500);
        response.setMsg(msg);
        return response;
    }
    public static <T> BaseResult<T> errorResponse() {
        BaseResult<T> response = new BaseResult<>();
        response.setData(null);
        response.setCode(500);
        response.setMsg("失败");
        return response;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java
New file
@@ -0,0 +1,79 @@
package com.ruoyi.common.core.domain;
import java.util.ArrayList;
import java.util.List;
/**
 * Tree基类
 *
 * @author ruoyi
 */
public class TreeEntity extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** 父菜单名称 */
    private String parentName;
    /** 父菜单ID */
    private Long parentId;
    /** 显示顺序 */
    private Integer orderNum;
    /** 祖级列表 */
    private String ancestors;
    /** 子部门 */
    private List<?> children = new ArrayList<>();
    public String getParentName()
    {
        return parentName;
    }
    public void setParentName(String parentName)
    {
        this.parentName = parentName;
    }
    public Long getParentId()
    {
        return parentId;
    }
    public void setParentId(Long parentId)
    {
        this.parentId = parentId;
    }
    public Integer getOrderNum()
    {
        return orderNum;
    }
    public void setOrderNum(Integer orderNum)
    {
        this.orderNum = orderNum;
    }
    public String getAncestors()
    {
        return ancestors;
    }
    public void setAncestors(String ancestors)
    {
        this.ancestors = ancestors;
    }
    public List<?> getChildren()
    {
        return children;
    }
    public void setChildren(List<?> children)
    {
        this.children = children;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
New file
@@ -0,0 +1,93 @@
package com.ruoyi.common.core.domain;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.utils.StringUtils;
/**
 * Treeselect树结构实体类
 *
 * @author ruoyi
 */
public class TreeSelect implements Serializable
{
    private static final long serialVersionUID = 1L;
    /** 节点ID */
    private Long id;
    /** 节点名称 */
    private String label;
    /** 节点禁用 */
    private boolean disabled = false;
    /** 子节点 */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<TreeSelect> children;
    public TreeSelect()
    {
    }
    public TreeSelect(SysDept dept)
    {
        this.id = dept.getDeptId();
        this.label = dept.getDeptName();
        this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus());
        this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
    }
    public TreeSelect(SysMenu menu)
    {
        this.id = menu.getMenuId();
        this.label = menu.getMenuName();
        this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
    }
    public Long getId()
    {
        return id;
    }
    public void setId(Long id)
    {
        this.id = id;
    }
    public String getLabel()
    {
        return label;
    }
    public void setLabel(String label)
    {
        this.label = label;
    }
    public boolean isDisabled()
    {
        return disabled;
    }
    public void setDisabled(boolean disabled)
    {
        this.disabled = disabled;
    }
    public List<TreeSelect> getChildren()
    {
        return children;
    }
    public void setChildren(List<TreeSelect> children)
    {
        this.children = children;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
New file
@@ -0,0 +1,203 @@
package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * 部门表 sys_dept
 *
 * @author ruoyi
 */
public class SysDept extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** 部门ID */
    private Long deptId;
    /** 父部门ID */
    private Long parentId;
    /** 祖级列表 */
    private String ancestors;
    /** 部门名称 */
    private String deptName;
    /** 显示顺序 */
    private Integer orderNum;
    /** 负责人 */
    private String leader;
    /** 联系电话 */
    private String phone;
    /** 邮箱 */
    private String email;
    /** 部门状态:0正常,1停用 */
    private String status;
    /** 删除标志(0代表存在 2代表删除) */
    private String delFlag;
    /** 父部门名称 */
    private String parentName;
    /** 子部门 */
    private List<SysDept> children = new ArrayList<SysDept>();
    public Long getDeptId()
    {
        return deptId;
    }
    public void setDeptId(Long deptId)
    {
        this.deptId = deptId;
    }
    public Long getParentId()
    {
        return parentId;
    }
    public void setParentId(Long parentId)
    {
        this.parentId = parentId;
    }
    public String getAncestors()
    {
        return ancestors;
    }
    public void setAncestors(String ancestors)
    {
        this.ancestors = ancestors;
    }
    @NotBlank(message = "部门名称不能为空")
    @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
    public String getDeptName()
    {
        return deptName;
    }
    public void setDeptName(String deptName)
    {
        this.deptName = deptName;
    }
    @NotNull(message = "显示顺序不能为空")
    public Integer getOrderNum()
    {
        return orderNum;
    }
    public void setOrderNum(Integer orderNum)
    {
        this.orderNum = orderNum;
    }
    public String getLeader()
    {
        return leader;
    }
    public void setLeader(String leader)
    {
        this.leader = leader;
    }
    @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
    public String getPhone()
    {
        return phone;
    }
    public void setPhone(String phone)
    {
        this.phone = phone;
    }
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    public String getEmail()
    {
        return email;
    }
    public void setEmail(String email)
    {
        this.email = email;
    }
    public String getStatus()
    {
        return status;
    }
    public void setStatus(String status)
    {
        this.status = status;
    }
    public String getDelFlag()
    {
        return delFlag;
    }
    public void setDelFlag(String delFlag)
    {
        this.delFlag = delFlag;
    }
    public String getParentName()
    {
        return parentName;
    }
    public void setParentName(String parentName)
    {
        this.parentName = parentName;
    }
    public List<SysDept> getChildren()
    {
        return children;
    }
    public void setChildren(List<SysDept> children)
    {
        this.children = children;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("deptId", getDeptId())
            .append("parentId", getParentId())
            .append("ancestors", getAncestors())
            .append("deptName", getDeptName())
            .append("orderNum", getOrderNum())
            .append("leader", getLeader())
            .append("phone", getPhone())
            .append("email", getEmail())
            .append("status", getStatus())
            .append("delFlag", getDelFlag())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
            .append("updateBy", getUpdateBy())
            .append("updateTime", getUpdateTime())
            .toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
New file
@@ -0,0 +1,176 @@
package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * 字典数据表 sys_dict_data
 *
 * @author ruoyi
 */
public class SysDictData extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** 字典编码 */
    @Excel(name = "字典编码", cellType = ColumnType.NUMERIC)
    private Long dictCode;
    /** 字典排序 */
    @Excel(name = "字典排序", cellType = ColumnType.NUMERIC)
    private Long dictSort;
    /** 字典标签 */
    @Excel(name = "字典标签")
    private String dictLabel;
    /** 字典键值 */
    @Excel(name = "字典键值")
    private String dictValue;
    /** 字典类型 */
    @Excel(name = "字典类型")
    private String dictType;
    /** 样式属性(其他样式扩展) */
    private String cssClass;
    /** 表格字典样式 */
    private String listClass;
    /** 是否默认(Y是 N否) */
    @Excel(name = "是否默认", readConverterExp = "Y=是,N=否")
    private String isDefault;
    /** 状态(0正常 1停用) */
    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
    private String status;
    public Long getDictCode()
    {
        return dictCode;
    }
    public void setDictCode(Long dictCode)
    {
        this.dictCode = dictCode;
    }
    public Long getDictSort()
    {
        return dictSort;
    }
    public void setDictSort(Long dictSort)
    {
        this.dictSort = dictSort;
    }
    @NotBlank(message = "字典标签不能为空")
    @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
    public String getDictLabel()
    {
        return dictLabel;
    }
    public void setDictLabel(String dictLabel)
    {
        this.dictLabel = dictLabel;
    }
    @NotBlank(message = "字典键值不能为空")
    @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
    public String getDictValue()
    {
        return dictValue;
    }
    public void setDictValue(String dictValue)
    {
        this.dictValue = dictValue;
    }
    @NotBlank(message = "字典类型不能为空")
    @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
    public String getDictType()
    {
        return dictType;
    }
    public void setDictType(String dictType)
    {
        this.dictType = dictType;
    }
    @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
    public String getCssClass()
    {
        return cssClass;
    }
    public void setCssClass(String cssClass)
    {
        this.cssClass = cssClass;
    }
    public String getListClass()
    {
        return listClass;
    }
    public void setListClass(String listClass)
    {
        this.listClass = listClass;
    }
    public boolean getDefault()
    {
        return UserConstants.YES.equals(this.isDefault);
    }
    public String getIsDefault()
    {
        return isDefault;
    }
    public void setIsDefault(String isDefault)
    {
        this.isDefault = isDefault;
    }
    public String getStatus()
    {
        return status;
    }
    public void setStatus(String status)
    {
        this.status = status;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("dictCode", getDictCode())
            .append("dictSort", getDictSort())
            .append("dictLabel", getDictLabel())
            .append("dictValue", getDictValue())
            .append("dictType", getDictType())
            .append("cssClass", getCssClass())
            .append("listClass", getListClass())
            .append("isDefault", getIsDefault())
            .append("status", getStatus())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
            .append("updateBy", getUpdateBy())
            .append("updateTime", getUpdateTime())
            .append("remark", getRemark())
            .toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
New file
@@ -0,0 +1,96 @@
package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * 字典类型表 sys_dict_type
 *
 * @author ruoyi
 */
public class SysDictType extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** 字典主键 */
    @Excel(name = "字典主键", cellType = ColumnType.NUMERIC)
    private Long dictId;
    /** 字典名称 */
    @Excel(name = "字典名称")
    private String dictName;
    /** 字典类型 */
    @Excel(name = "字典类型")
    private String dictType;
    /** 状态(0正常 1停用) */
    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
    private String status;
    public Long getDictId()
    {
        return dictId;
    }
    public void setDictId(Long dictId)
    {
        this.dictId = dictId;
    }
    @NotBlank(message = "字典名称不能为空")
    @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
    public String getDictName()
    {
        return dictName;
    }
    public void setDictName(String dictName)
    {
        this.dictName = dictName;
    }
    @NotBlank(message = "字典类型不能为空")
    @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
    @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
    public String getDictType()
    {
        return dictType;
    }
    public void setDictType(String dictType)
    {
        this.dictType = dictType;
    }
    public String getStatus()
    {
        return status;
    }
    public void setStatus(String status)
    {
        this.status = status;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("dictId", getDictId())
            .append("dictName", getDictName())
            .append("dictType", getDictType())
            .append("status", getStatus())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
            .append("updateBy", getUpdateBy())
            .append("updateTime", getUpdateTime())
            .append("remark", getRemark())
            .toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
New file
@@ -0,0 +1,274 @@
package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * 菜单权限表 sys_menu
 *
 * @author ruoyi
 */
public class SysMenu extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** 菜单ID */
    private Long menuId;
    /** 菜单名称 */
    private String menuName;
    /** 父菜单名称 */
    private String parentName;
    /** 父菜单ID */
    private Long parentId;
    /** 显示顺序 */
    private Integer orderNum;
    /** 路由地址 */
    private String path;
    /** 组件路径 */
    private String component;
    /** 路由参数 */
    private String query;
    /** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */
    private String routeName;
    /** 是否为外链(0是 1否) */
    private String isFrame;
    /** 是否缓存(0缓存 1不缓存) */
    private String isCache;
    /** 类型(M目录 C菜单 F按钮) */
    private String menuType;
    /** 显示状态(0显示 1隐藏) */
    private String visible;
    /** 菜单状态(0正常 1停用) */
    private String status;
    /** 权限字符串 */
    private String perms;
    /** 菜单图标 */
    private String icon;
    /** 子菜单 */
    private List<SysMenu> children = new ArrayList<SysMenu>();
    public Long getMenuId()
    {
        return menuId;
    }
    public void setMenuId(Long menuId)
    {
        this.menuId = menuId;
    }
    @NotBlank(message = "菜单名称不能为空")
    @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
    public String getMenuName()
    {
        return menuName;
    }
    public void setMenuName(String menuName)
    {
        this.menuName = menuName;
    }
    public String getParentName()
    {
        return parentName;
    }
    public void setParentName(String parentName)
    {
        this.parentName = parentName;
    }
    public Long getParentId()
    {
        return parentId;
    }
    public void setParentId(Long parentId)
    {
        this.parentId = parentId;
    }
    @NotNull(message = "显示顺序不能为空")
    public Integer getOrderNum()
    {
        return orderNum;
    }
    public void setOrderNum(Integer orderNum)
    {
        this.orderNum = orderNum;
    }
    @Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
    public String getPath()
    {
        return path;
    }
    public void setPath(String path)
    {
        this.path = path;
    }
    @Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
    public String getComponent()
    {
        return component;
    }
    public void setComponent(String component)
    {
        this.component = component;
    }
    public String getQuery()
    {
        return query;
    }
    public void setQuery(String query)
    {
        this.query = query;
    }
    public String getRouteName()
    {
        return routeName;
    }
    public void setRouteName(String routeName)
    {
        this.routeName = routeName;
    }
    public String getIsFrame()
    {
        return isFrame;
    }
    public void setIsFrame(String isFrame)
    {
        this.isFrame = isFrame;
    }
    public String getIsCache()
    {
        return isCache;
    }
    public void setIsCache(String isCache)
    {
        this.isCache = isCache;
    }
    @NotBlank(message = "菜单类型不能为空")
    public String getMenuType()
    {
        return menuType;
    }
    public void setMenuType(String menuType)
    {
        this.menuType = menuType;
    }
    public String getVisible()
    {
        return visible;
    }
    public void setVisible(String visible)
    {
        this.visible = visible;
    }
    public String getStatus()
    {
        return status;
    }
    public void setStatus(String status)
    {
        this.status = status;
    }
    @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
    public String getPerms()
    {
        return perms;
    }
    public void setPerms(String perms)
    {
        this.perms = perms;
    }
    public String getIcon()
    {
        return icon;
    }
    public void setIcon(String icon)
    {
        this.icon = icon;
    }
    public List<SysMenu> getChildren()
    {
        return children;
    }
    public void setChildren(List<SysMenu> children)
    {
        this.children = children;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("menuId", getMenuId())
            .append("menuName", getMenuName())
            .append("parentId", getParentId())
            .append("orderNum", getOrderNum())
            .append("path", getPath())
            .append("component", getComponent())
            .append("query", getQuery())
            .append("routeName", getRouteName())
            .append("isFrame", getIsFrame())
            .append("IsCache", getIsCache())
            .append("menuType", getMenuType())
            .append("visible", getVisible())
            .append("status ", getStatus())
            .append("perms", getPerms())
            .append("icon", getIcon())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
            .append("updateBy", getUpdateBy())
            .append("updateTime", getUpdateTime())
            .append("remark", getRemark())
            .toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
New file
@@ -0,0 +1,240 @@
package com.ruoyi.common.core.domain.entity;
import java.util.Set;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * 角色表 sys_role
 *
 * @author ruoyi
 */
public class SysRole extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** 角色ID */
    @Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
    private Long roleId;
    /** 角色名称 */
    @Excel(name = "角色名称")
    private String roleName;
    /** 角色权限 */
    @Excel(name = "角色权限")
    private String roleKey;
    /** 角色排序 */
    @Excel(name = "角色排序")
    private Integer roleSort;
    /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */
    @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
    private String dataScope;
    /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
    private boolean menuCheckStrictly;
    /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */
    private boolean deptCheckStrictly;
    /** 角色状态(0正常 1停用) */
    @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /** 删除标志(0代表存在 2代表删除) */
    private String delFlag;
    /** 用户是否存在此角色标识 默认不存在 */
    private boolean flag = false;
    /** 菜单组 */
    private Long[] menuIds;
    /** 部门组(数据权限) */
    private Long[] deptIds;
    /** 角色菜单权限 */
    private Set<String> permissions;
    public SysRole()
    {
    }
    public SysRole(Long roleId)
    {
        this.roleId = roleId;
    }
    public Long getRoleId()
    {
        return roleId;
    }
    public void setRoleId(Long roleId)
    {
        this.roleId = roleId;
    }
    public boolean isAdmin()
    {
        return isAdmin(this.roleId);
    }
    public static boolean isAdmin(Long roleId)
    {
        return roleId != null && 1L == roleId;
    }
    @NotBlank(message = "角色名称不能为空")
    @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
    public String getRoleName()
    {
        return roleName;
    }
    public void setRoleName(String roleName)
    {
        this.roleName = roleName;
    }
    @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
    public String getRoleKey()
    {
        return roleKey;
    }
    public void setRoleKey(String roleKey)
    {
        this.roleKey = roleKey;
    }
    @NotNull(message = "显示顺序不能为空")
    public Integer getRoleSort()
    {
        return roleSort;
    }
    public void setRoleSort(Integer roleSort)
    {
        this.roleSort = roleSort;
    }
    public String getDataScope()
    {
        return dataScope;
    }
    public void setDataScope(String dataScope)
    {
        this.dataScope = dataScope;
    }
    public boolean isMenuCheckStrictly()
    {
        return menuCheckStrictly;
    }
    public void setMenuCheckStrictly(boolean menuCheckStrictly)
    {
        this.menuCheckStrictly = menuCheckStrictly;
    }
    public boolean isDeptCheckStrictly()
    {
        return deptCheckStrictly;
    }
    public void setDeptCheckStrictly(boolean deptCheckStrictly)
    {
        this.deptCheckStrictly = deptCheckStrictly;
    }
    public String getStatus()
    {
        return status;
    }
    public void setStatus(String status)
    {
        this.status = status;
    }
    public String getDelFlag()
    {
        return delFlag;
    }
    public void setDelFlag(String delFlag)
    {
        this.delFlag = delFlag;
    }
    public boolean isFlag()
    {
        return flag;
    }
    public void setFlag(boolean flag)
    {
        this.flag = flag;
    }
    public Long[] getMenuIds()
    {
        return menuIds;
    }
    public void setMenuIds(Long[] menuIds)
    {
        this.menuIds = menuIds;
    }
    public Long[] getDeptIds()
    {
        return deptIds;
    }
    public void setDeptIds(Long[] deptIds)
    {
        this.deptIds = deptIds;
    }
    public Set<String> getPermissions()
    {
        return permissions;
    }
    public void setPermissions(Set<String> permissions)
    {
        this.permissions = permissions;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("roleId", getRoleId())
            .append("roleName", getRoleName())
            .append("roleKey", getRoleKey())
            .append("roleSort", getRoleSort())
            .append("dataScope", getDataScope())
            .append("menuCheckStrictly", isMenuCheckStrictly())
            .append("deptCheckStrictly", isDeptCheckStrictly())
            .append("status", getStatus())
            .append("delFlag", getDelFlag())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
            .append("updateBy", getUpdateBy())
            .append("updateTime", getUpdateTime())
            .append("remark", getRemark())
            .toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
New file
@@ -0,0 +1,325 @@
package com.ruoyi.common.core.domain.entity;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.*;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type;
import com.ruoyi.common.annotation.Excels;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.xss.Xss;
/**
 * 用户对象 sys_user
 *
 * @author ruoyi
 */
public class SysUser extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * 用户ID
     */
    @ApiModelProperty(value = "用户id")
    @Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
    private Long userId;
    /**
     * 部门ID
     */
    @Excel(name = "部门编号", type = Type.IMPORT)
    private Long deptId;
    /**
     * 用户账号
     */
    @ApiModelProperty(value = "账号")
    @Excel(name = "登录名称")
    private String userName;
    /**
     * 用户昵称
     */
    @ApiModelProperty(value = "账号名称")
    @Excel(name = "用户名称")
    private String nickName;
    /**
     * 用户邮箱
     */
    @Excel(name = "用户邮箱")
    private String email;
    /**
     * 手机号码
     */
    @Excel(name = "手机号码", cellType = ColumnType.TEXT)
    private String phonenumber;
    /**
     * 用户性别
     */
    @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
    private String sex;
    /**
     * 用户头像
     */
    private String avatar;
    /**
     * 密码
     */
    private String password;
    /**
     * 帐号状态(0正常 1停用)
     */
    @ApiModelProperty(value = "账号状态 0=正常,1=停用")
    @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /**
     * 删除标志(0代表存在 2代表删除)
     */
    private String delFlag;
    /**
     * 最后登录IP
     */
    @Excel(name = "最后登录IP", type = Type.EXPORT)
    private String loginIp;
    /**
     * 最后登录时间
     */
    @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
    private Date loginDate;
    /**
     * 部门对象
     */
    @Excels({
            @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
            @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
    })
    private SysDept dept;
    /**
     * 角色对象
     */
    private List<SysRole> roles;
    /**
     * 角色组
     */
    private Long[] roleIds;
    /**
     * 岗位组
     */
    private Long[] postIds;
    /**
     * 角色ID
     */
    private Long roleId;
    public SysUser() {
    }
    public SysUser(Long userId) {
        this.userId = userId;
    }
    public Long getUserId() {
        return userId;
    }
    public void setUserId(Long userId) {
        this.userId = userId;
    }
    public boolean isAdmin() {
        return isAdmin(this.userId);
    }
    public static boolean isAdmin(Long userId) {
        return userId != null && 1L == userId;
    }
    public Long getDeptId() {
        return deptId;
    }
    public void setDeptId(Long deptId) {
        this.deptId = deptId;
    }
    @Xss(message = "用户昵称不能包含脚本字符")
    @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
    public String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
    @Xss(message = "用户账号不能包含脚本字符")
    @NotBlank(message = "用户账号不能为空")
    @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
    public String getPhonenumber() {
        return phonenumber;
    }
    public void setPhonenumber(String phonenumber) {
        this.phonenumber = phonenumber;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAvatar() {
        return avatar;
    }
    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public String getDelFlag() {
        return delFlag;
    }
    public void setDelFlag(String delFlag) {
        this.delFlag = delFlag;
    }
    public String getLoginIp() {
        return loginIp;
    }
    public void setLoginIp(String loginIp) {
        this.loginIp = loginIp;
    }
    public Date getLoginDate() {
        return loginDate;
    }
    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }
    public SysDept getDept() {
        return dept;
    }
    public void setDept(SysDept dept) {
        this.dept = dept;
    }
    public List<SysRole> getRoles() {
        return roles;
    }
    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }
    public Long[] getRoleIds() {
        return roleIds;
    }
    public void setRoleIds(Long[] roleIds) {
        this.roleIds = roleIds;
    }
    public Long[] getPostIds() {
        return postIds;
    }
    public void setPostIds(Long[] postIds) {
        this.postIds = postIds;
    }
    public Long getRoleId() {
        return roleId;
    }
    public void setRoleId(Long roleId) {
        this.roleId = roleId;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("userId", getUserId())
                .append("deptId", getDeptId())
                .append("userName", getUserName())
                .append("nickName", getNickName())
                .append("email", getEmail())
                .append("phonenumber", getPhonenumber())
                .append("sex", getSex())
                .append("avatar", getAvatar())
                .append("password", getPassword())
                .append("status", getStatus())
                .append("delFlag", getDelFlag())
                .append("loginIp", getLoginIp())
                .append("loginDate", getLoginDate())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .append("remark", getRemark())
                .append("dept", getDept())
                .toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
New file
@@ -0,0 +1,66 @@
package com.ruoyi.common.core.domain.model;
import io.swagger.annotations.ApiModelProperty;
/**
 * 用户登录对象
 *
 * @author ruoyi
 */
public class LoginBody {
    /**
     * 用户名
     */
    @ApiModelProperty(value = "用户名称")
    private String username;
    /**
     * 用户密码
     */
    @ApiModelProperty(value = "用户密码")
    private String password;
    /**
     * 验证码
     */
    @ApiModelProperty(value = "验证码")
    private String code;
    /**
     * 唯一标识
     */
    @ApiModelProperty(value = "验证码接口UUID")
    private String uuid;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getUuid() {
        return uuid;
    }
    public void setUuid(String uuid) {
        this.uuid = uuid;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
New file
@@ -0,0 +1,266 @@
package com.ruoyi.common.core.domain.model;
import com.alibaba.fastjson2.annotation.JSONField;
import com.ruoyi.common.core.domain.entity.SysUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
/**
 * 登录用户身份权限
 *
 * @author ruoyi
 */
public class LoginUser implements UserDetails
{
    private static final long serialVersionUID = 1L;
    /**
     * 用户ID
     */
    private Long userId;
    /**
     * 部门ID
     */
    private Long deptId;
    /**
     * 用户唯一标识
     */
    private String token;
    /**
     * 登录时间
     */
    private Long loginTime;
    /**
     * 过期时间
     */
    private Long expireTime;
    /**
     * 登录IP地址
     */
    private String ipaddr;
    /**
     * 登录地点
     */
    private String loginLocation;
    /**
     * 浏览器类型
     */
    private String browser;
    /**
     * 操作系统
     */
    private String os;
    /**
     * 权限列表
     */
    private Set<String> permissions;
    /**
     * 用户信息
     */
    private SysUser user;
    public LoginUser()
    {
    }
    public LoginUser(SysUser user, Set<String> permissions)
    {
        this.user = user;
        this.permissions = permissions;
    }
    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
    {
        this.userId = userId;
        this.deptId = deptId;
        this.user = user;
        this.permissions = permissions;
    }
    public Long getUserId()
    {
        return userId;
    }
    public void setUserId(Long userId)
    {
        this.userId = userId;
    }
    public Long getDeptId()
    {
        return deptId;
    }
    public void setDeptId(Long deptId)
    {
        this.deptId = deptId;
    }
    public String getToken()
    {
        return token;
    }
    public void setToken(String token)
    {
        this.token = token;
    }
    @JSONField(serialize = false)
    @Override
    public String getPassword()
    {
        return user.getPassword();
    }
    @Override
    public String getUsername()
    {
        return user.getUserName();
    }
    /**
     * 账户是否未过期,过期无法验证
     */
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonExpired()
    {
        return true;
    }
    /**
     * 指定用户是否解锁,锁定的用户无法进行身份验证
     *
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonLocked()
    {
        return true;
    }
    /**
     * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
     *
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isCredentialsNonExpired()
    {
        return true;
    }
    /**
     * 是否可用 ,禁用的用户不能身份验证
     *
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isEnabled()
    {
        return true;
    }
    public Long getLoginTime()
    {
        return loginTime;
    }
    public void setLoginTime(Long loginTime)
    {
        this.loginTime = loginTime;
    }
    public String getIpaddr()
    {
        return ipaddr;
    }
    public void setIpaddr(String ipaddr)
    {
        this.ipaddr = ipaddr;
    }
    public String getLoginLocation()
    {
        return loginLocation;
    }
    public void setLoginLocation(String loginLocation)
    {
        this.loginLocation = loginLocation;
    }
    public String getBrowser()
    {
        return browser;
    }
    public void setBrowser(String browser)
    {
        this.browser = browser;
    }
    public String getOs()
    {
        return os;
    }
    public void setOs(String os)
    {
        this.os = os;
    }
    public Long getExpireTime()
    {
        return expireTime;
    }
    public void setExpireTime(Long expireTime)
    {
        this.expireTime = expireTime;
    }
    public Set<String> getPermissions()
    {
        return permissions;
    }
    public void setPermissions(Set<String> permissions)
    {
        this.permissions = permissions;
    }
    public SysUser getUser()
    {
        return user;
    }
    public void setUser(SysUser user)
    {
        this.user = user;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
    {
        return null;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java
New file
@@ -0,0 +1,11 @@
package com.ruoyi.common.core.domain.model;
/**
 * 用户注册对象
 *
 * @author ruoyi
 */
public class RegisterBody extends LoginBody
{
}
pt-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java
New file
@@ -0,0 +1,104 @@
package com.ruoyi.common.core.page;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.ApiModelProperty;
/**
 * 分页数据
 *
 * @author ruoyi
 */
public class PageDomain
{
    /** 当前记录起始索引 */
    @ApiModelProperty(value = "当前页")
    private Integer pageNum;
    /** 每页显示记录数 */
    @ApiModelProperty(value = "记录数")
    private Integer pageSize;
    /** 排序列 */
    private String orderByColumn;
    /** 排序的方向desc或者asc */
    private String isAsc = "asc";
    /** 分页参数合理化 */
    private Boolean reasonable = true;
    public String getOrderBy()
    {
        if (StringUtils.isEmpty(orderByColumn))
        {
            return "";
        }
        return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
    }
    public Integer getPageNum()
    {
        return pageNum;
    }
    public void setPageNum(Integer pageNum)
    {
        this.pageNum = pageNum;
    }
    public Integer getPageSize()
    {
        return pageSize;
    }
    public void setPageSize(Integer pageSize)
    {
        this.pageSize = pageSize;
    }
    public String getOrderByColumn()
    {
        return orderByColumn;
    }
    public void setOrderByColumn(String orderByColumn)
    {
        this.orderByColumn = orderByColumn;
    }
    public String getIsAsc()
    {
        return isAsc;
    }
    public void setIsAsc(String isAsc)
    {
        if (StringUtils.isNotEmpty(isAsc))
        {
            // 兼容前端排序类型
            if ("ascending".equals(isAsc))
            {
                isAsc = "asc";
            }
            else if ("descending".equals(isAsc))
            {
                isAsc = "desc";
            }
            this.isAsc = isAsc;
        }
    }
    public Boolean getReasonable()
    {
        if (StringUtils.isNull(reasonable))
        {
            return Boolean.TRUE;
        }
        return reasonable;
    }
    public void setReasonable(Boolean reasonable)
    {
        this.reasonable = reasonable;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
New file
@@ -0,0 +1,85 @@
package com.ruoyi.common.core.page;
import java.io.Serializable;
import java.util.List;
/**
 * 表格分页数据对象
 *
 * @author ruoyi
 */
public class TableDataInfo implements Serializable
{
    private static final long serialVersionUID = 1L;
    /** 总记录数 */
    private long total;
    /** 列表数据 */
    private List<?> rows;
    /** 消息状态码 */
    private int code;
    /** 消息内容 */
    private String msg;
    /**
     * 表格数据对象
     */
    public TableDataInfo()
    {
    }
    /**
     * 分页
     *
     * @param list 列表数据
     * @param total 总记录数
     */
    public TableDataInfo(List<?> list, long total)
    {
        this.rows = list;
        this.total = total;
    }
    public long getTotal()
    {
        return total;
    }
    public void setTotal(long total)
    {
        this.total = total;
    }
    public List<?> getRows()
    {
        return rows;
    }
    public void setRows(List<?> rows)
    {
        this.rows = rows;
    }
    public int getCode()
    {
        return code;
    }
    public void setCode(int code)
    {
        this.code = code;
    }
    public String getMsg()
    {
        return msg;
    }
    public void setMsg(String msg)
    {
        this.msg = msg;
    }
}
pt-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java
New file
@@ -0,0 +1,56 @@
package com.ruoyi.common.core.page;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.ServletUtils;
/**
 * 表格数据处理
 *
 * @author ruoyi
 */
public class TableSupport
{
    /**
     * 当前记录起始索引
     */
    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 static final String REASONABLE = "reasonable";
    /**
     * 封装分页对象
     */
    public static PageDomain getPageDomain()
    {
        PageDomain pageDomain = new PageDomain();
        pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
        pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
        pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
        pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
        pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
        return pageDomain;
    }
    public static PageDomain buildPageRequest()
    {
        return getPageDomain();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
New file
@@ -0,0 +1,268 @@
package com.ruoyi.common.core.redis;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
 * spring redis 工具类
 *
 * @author ruoyi
 **/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    public long getExpire(final String key)
    {
        return redisTemplate.getExpire(key);
    }
    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String key)
    {
        return redisTemplate.hasKey(key);
    }
    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }
    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }
    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public boolean deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection) > 0;
    }
    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }
    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }
    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }
    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }
    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }
    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }
    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * 删除Hash中的某条数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    public boolean deleteCacheMapValue(final String key, final String hKey)
    {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }
    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}
pt-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java
New file
@@ -0,0 +1,86 @@
package com.ruoyi.common.core.text;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import com.ruoyi.common.utils.StringUtils;
/**
 * 字符集工具类
 *
 * @author ruoyi
 */
public class CharsetKit
{
    /** ISO-8859-1 */
    public static final String ISO_8859_1 = "ISO-8859-1";
    /** UTF-8 */
    public static final String UTF_8 = "UTF-8";
    /** GBK */
    public static final String GBK = "GBK";
    /** ISO-8859-1 */
    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
    /** UTF-8 */
    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
    /** GBK */
    public static final Charset CHARSET_GBK = Charset.forName(GBK);
    /**
     * 转换为Charset对象
     *
     * @param charset 字符集,为空则返回默认字符集
     * @return Charset
     */
    public static Charset charset(String charset)
    {
        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
    }
    /**
     * 转换字符串的字符集编码
     *
     * @param source 字符串
     * @param srcCharset 源字符集,默认ISO-8859-1
     * @param destCharset 目标字符集,默认UTF-8
     * @return 转换后的字符集
     */
    public static String convert(String source, String srcCharset, String destCharset)
    {
        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
    }
    /**
     * 转换字符串的字符集编码
     *
     * @param source 字符串
     * @param srcCharset 源字符集,默认ISO-8859-1
     * @param destCharset 目标字符集,默认UTF-8
     * @return 转换后的字符集
     */
    public static String convert(String source, Charset srcCharset, Charset destCharset)
    {
        if (null == srcCharset)
        {
            srcCharset = StandardCharsets.ISO_8859_1;
        }
        if (null == destCharset)
        {
            destCharset = StandardCharsets.UTF_8;
        }
        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
        {
            return source;
        }
        return new String(source.getBytes(srcCharset), destCharset);
    }
    /**
     * @return 系统字符集编码
     */
    public static String systemCharset()
    {
        return Charset.defaultCharset().name();
    }
}
pt-common/src/main/java/com/ruoyi/common/core/text/Convert.java
New file
@@ -0,0 +1,1012 @@
package com.ruoyi.common.core.text;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.Set;
import com.ruoyi.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
/**
 * 类型转换器
 *
 * @author ruoyi
 */
public class Convert
{
    /**
     * 转换为字符串<br>
     * 如果给定的值为null,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static String toStr(Object value, String defaultValue)
    {
        if (null == value)
        {
            return defaultValue;
        }
        if (value instanceof String)
        {
            return (String) value;
        }
        return value.toString();
    }
    /**
     * 转换为字符串<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static String toStr(Object value)
    {
        return toStr(value, null);
    }
    /**
     * 转换为字符<br>
     * 如果给定的值为null,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Character toChar(Object value, Character defaultValue)
    {
        if (null == value)
        {
            return defaultValue;
        }
        if (value instanceof Character)
        {
            return (Character) value;
        }
        final String valueStr = toStr(value, null);
        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
    }
    /**
     * 转换为字符<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Character toChar(Object value)
    {
        return toChar(value, null);
    }
    /**
     * 转换为byte<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Byte toByte(Object value, Byte defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Byte)
        {
            return (Byte) value;
        }
        if (value instanceof Number)
        {
            return ((Number) value).byteValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return Byte.parseByte(valueStr);
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为byte<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Byte toByte(Object value)
    {
        return toByte(value, null);
    }
    /**
     * 转换为Short<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Short toShort(Object value, Short defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Short)
        {
            return (Short) value;
        }
        if (value instanceof Number)
        {
            return ((Number) value).shortValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return Short.parseShort(valueStr.trim());
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为Short<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Short toShort(Object value)
    {
        return toShort(value, null);
    }
    /**
     * 转换为Number<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Number toNumber(Object value, Number defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Number)
        {
            return (Number) value;
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return NumberFormat.getInstance().parse(valueStr);
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为Number<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Number toNumber(Object value)
    {
        return toNumber(value, null);
    }
    /**
     * 转换为int<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Integer toInt(Object value, Integer defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Integer)
        {
            return (Integer) value;
        }
        if (value instanceof Number)
        {
            return ((Number) value).intValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return Integer.parseInt(valueStr.trim());
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为int<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Integer toInt(Object value)
    {
        return toInt(value, null);
    }
    /**
     * 转换为Integer数组<br>
     *
     * @param str 被转换的值
     * @return 结果
     */
    public static Integer[] toIntArray(String str)
    {
        return toIntArray(",", str);
    }
    /**
     * 转换为Long数组<br>
     *
     * @param str 被转换的值
     * @return 结果
     */
    public static Long[] toLongArray(String str)
    {
        return toLongArray(",", str);
    }
    /**
     * 转换为Integer数组<br>
     *
     * @param split 分隔符
     * @param split 被转换的值
     * @return 结果
     */
    public static Integer[] toIntArray(String split, String str)
    {
        if (StringUtils.isEmpty(str))
        {
            return new Integer[] {};
        }
        String[] arr = str.split(split);
        final Integer[] ints = new Integer[arr.length];
        for (int i = 0; i < arr.length; i++)
        {
            final Integer v = toInt(arr[i], 0);
            ints[i] = v;
        }
        return ints;
    }
    /**
     * 转换为Long数组<br>
     *
     * @param split 分隔符
     * @param str 被转换的值
     * @return 结果
     */
    public static Long[] toLongArray(String split, String str)
    {
        if (StringUtils.isEmpty(str))
        {
            return new Long[] {};
        }
        String[] arr = str.split(split);
        final Long[] longs = new Long[arr.length];
        for (int i = 0; i < arr.length; i++)
        {
            final Long v = toLong(arr[i], null);
            longs[i] = v;
        }
        return longs;
    }
    /**
     * 转换为String数组<br>
     *
     * @param str 被转换的值
     * @return 结果
     */
    public static String[] toStrArray(String str)
    {
        if (StringUtils.isEmpty(str))
        {
            return new String[] {};
        }
        return toStrArray(",", str);
    }
    /**
     * 转换为String数组<br>
     *
     * @param split 分隔符
     * @param split 被转换的值
     * @return 结果
     */
    public static String[] toStrArray(String split, String str)
    {
        return str.split(split);
    }
    /**
     * 转换为long<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Long toLong(Object value, Long defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Long)
        {
            return (Long) value;
        }
        if (value instanceof Number)
        {
            return ((Number) value).longValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            // 支持科学计数法
            return new BigDecimal(valueStr.trim()).longValue();
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为long<br>
     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Long toLong(Object value)
    {
        return toLong(value, null);
    }
    /**
     * 转换为double<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Double toDouble(Object value, Double defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Double)
        {
            return (Double) value;
        }
        if (value instanceof Number)
        {
            return ((Number) value).doubleValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            // 支持科学计数法
            return new BigDecimal(valueStr.trim()).doubleValue();
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为double<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Double toDouble(Object value)
    {
        return toDouble(value, null);
    }
    /**
     * 转换为Float<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Float toFloat(Object value, Float defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Float)
        {
            return (Float) value;
        }
        if (value instanceof Number)
        {
            return ((Number) value).floatValue();
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return Float.parseFloat(valueStr.trim());
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为Float<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Float toFloat(Object value)
    {
        return toFloat(value, null);
    }
    /**
     * 转换为boolean<br>
     * String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static Boolean toBool(Object value, Boolean defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof Boolean)
        {
            return (Boolean) value;
        }
        String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        valueStr = valueStr.trim().toLowerCase();
        switch (valueStr)
        {
            case "true":
            case "yes":
            case "ok":
            case "1":
            case "是":
                return true;
            case "false":
            case "no":
            case "0":
            case "否":
                return false;
            default:
                return defaultValue;
        }
    }
    /**
     * 转换为boolean<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static Boolean toBool(Object value)
    {
        return toBool(value, null);
    }
    /**
     * 转换为Enum对象<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     *
     * @param clazz Enum的Class
     * @param value 值
     * @param defaultValue 默认值
     * @return Enum
     */
    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (clazz.isAssignableFrom(value.getClass()))
        {
            @SuppressWarnings("unchecked")
            E myE = (E) value;
            return myE;
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return Enum.valueOf(clazz, valueStr);
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为Enum对象<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     *
     * @param clazz Enum的Class
     * @param value 值
     * @return Enum
     */
    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value)
    {
        return toEnum(clazz, value, null);
    }
    /**
     * 转换为BigInteger<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static BigInteger toBigInteger(Object value, BigInteger defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof BigInteger)
        {
            return (BigInteger) value;
        }
        if (value instanceof Long)
        {
            return BigInteger.valueOf((Long) value);
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return new BigInteger(valueStr);
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为BigInteger<br>
     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static BigInteger toBigInteger(Object value)
    {
        return toBigInteger(value, null);
    }
    /**
     * 转换为BigDecimal<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return 结果
     */
    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue)
    {
        if (value == null)
        {
            return defaultValue;
        }
        if (value instanceof BigDecimal)
        {
            return (BigDecimal) value;
        }
        if (value instanceof Long)
        {
            return new BigDecimal((Long) value);
        }
        if (value instanceof Double)
        {
            return BigDecimal.valueOf((Double) value);
        }
        if (value instanceof Integer)
        {
            return new BigDecimal((Integer) value);
        }
        final String valueStr = toStr(value, null);
        if (StringUtils.isEmpty(valueStr))
        {
            return defaultValue;
        }
        try
        {
            return new BigDecimal(valueStr);
        }
        catch (Exception e)
        {
            return defaultValue;
        }
    }
    /**
     * 转换为BigDecimal<br>
     * 如果给定的值为空,或者转换失败,返回默认值<br>
     * 转换失败不会报错
     *
     * @param value 被转换的值
     * @return 结果
     */
    public static BigDecimal toBigDecimal(Object value)
    {
        return toBigDecimal(value, null);
    }
    /**
     * 将对象转为字符串<br>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
     *
     * @param obj 对象
     * @return 字符串
     */
    public static String utf8Str(Object obj)
    {
        return str(obj, CharsetKit.CHARSET_UTF_8);
    }
    /**
     * 将对象转为字符串<br>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
     *
     * @param obj 对象
     * @param charsetName 字符集
     * @return 字符串
     */
    public static String str(Object obj, String charsetName)
    {
        return str(obj, Charset.forName(charsetName));
    }
    /**
     * 将对象转为字符串<br>
     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
     *
     * @param obj 对象
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(Object obj, Charset charset)
    {
        if (null == obj)
        {
            return null;
        }
        if (obj instanceof String)
        {
            return (String) obj;
        }
        else if (obj instanceof byte[])
        {
            return str((byte[]) obj, charset);
        }
        else if (obj instanceof Byte[])
        {
            byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
            return str(bytes, charset);
        }
        else if (obj instanceof ByteBuffer)
        {
            return str((ByteBuffer) obj, charset);
        }
        return obj.toString();
    }
    /**
     * 将byte数组转为字符串
     *
     * @param bytes byte数组
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(byte[] bytes, String charset)
    {
        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
    }
    /**
     * 解码字节码
     *
     * @param data 字符串
     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
     * @return 解码后的字符串
     */
    public static String str(byte[] data, Charset charset)
    {
        if (data == null)
        {
            return null;
        }
        if (null == charset)
        {
            return new String(data);
        }
        return new String(data, charset);
    }
    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data 数据
     * @param charset 字符集,如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, String charset)
    {
        if (data == null)
        {
            return null;
        }
        return str(data, Charset.forName(charset));
    }
    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data 数据
     * @param charset 字符集,如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, Charset charset)
    {
        if (null == charset)
        {
            charset = Charset.defaultCharset();
        }
        return charset.decode(data).toString();
    }
    // ----------------------------------------------------------------------- 全角半角转换
    /**
     * 半角转全角
     *
     * @param input String.
     * @return 全角字符串.
     */
    public static String toSBC(String input)
    {
        return toSBC(input, null);
    }
    /**
     * 半角转全角
     *
     * @param input String
     * @param notConvertSet 不替换的字符集合
     * @return 全角字符串.
     */
    public static String toSBC(String input, Set<Character> notConvertSet)
    {
        char[] c = input.toCharArray();
        for (int i = 0; i < c.length; i++)
        {
            if (null != notConvertSet && notConvertSet.contains(c[i]))
            {
                // 跳过不替换的字符
                continue;
            }
            if (c[i] == ' ')
            {
                c[i] = '\u3000';
            }
            else if (c[i] < '\177')
            {
                c[i] = (char) (c[i] + 65248);
            }
        }
        return new String(c);
    }
    /**
     * 全角转半角
     *
     * @param input String.
     * @return 半角字符串
     */
    public static String toDBC(String input)
    {
        return toDBC(input, null);
    }
    /**
     * 替换全角为半角
     *
     * @param text 文本
     * @param notConvertSet 不替换的字符集合
     * @return 替换后的字符
     */
    public static String toDBC(String text, Set<Character> notConvertSet)
    {
        char[] c = text.toCharArray();
        for (int i = 0; i < c.length; i++)
        {
            if (null != notConvertSet && notConvertSet.contains(c[i]))
            {
                // 跳过不替换的字符
                continue;
            }
            if (c[i] == '\u3000')
            {
                c[i] = ' ';
            }
            else if (c[i] > '\uFF00' && c[i] < '\uFF5F')
            {
                c[i] = (char) (c[i] - 65248);
            }
        }
        String returnString = new String(c);
        return returnString;
    }
    /**
     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
     *
     * @param n 数字
     * @return 中文大写数字
     */
    public static String digitUppercase(double n)
    {
        String[] fraction = { "角", "分" };
        String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
        String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };
        String head = n < 0 ? "负" : "";
        n = Math.abs(n);
        String s = "";
        for (int i = 0; i < fraction.length; i++)
        {
            // 优化double计算精度丢失问题
            BigDecimal nNum = new BigDecimal(n);
            BigDecimal decimal = new BigDecimal(10);
            BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
            double d = scale.doubleValue();
            s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
        }
        if (s.length() < 1)
        {
            s = "整";
        }
        int integerPart = (int) Math.floor(n);
        for (int i = 0; i < unit[0].length && integerPart > 0; i++)
        {
            String p = "";
            for (int j = 0; j < unit[1].length && n > 0; j++)
            {
                p = digit[integerPart % 10] + unit[1][j] + p;
                integerPart = integerPart / 10;
            }
            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
        }
        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
    }
}
pt-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java
New file
@@ -0,0 +1,92 @@
package com.ruoyi.common.core.text;
import com.ruoyi.common.utils.StringUtils;
/**
 * 字符串格式化
 *
 * @author ruoyi
 */
public class StrFormatter
{
    public static final String EMPTY_JSON = "{}";
    public static final char C_BACKSLASH = '\\';
    public static final char C_DELIM_START = '{';
    public static final char C_DELIM_END = '}';
    /**
     * 格式化字符串<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例:<br>
     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param strPattern 字符串模板
     * @param argArray 参数列表
     * @return 结果
     */
    public static String format(final String strPattern, final Object... argArray)
    {
        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray))
        {
            return strPattern;
        }
        final int strPatternLength = strPattern.length();
        // 初始化定义好的长度以获得更好的性能
        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
        int handledPosition = 0;
        int delimIndex;// 占位符所在位置
        for (int argIndex = 0; argIndex < argArray.length; argIndex++)
        {
            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
            if (delimIndex == -1)
            {
                if (handledPosition == 0)
                {
                    return strPattern;
                }
                else
                { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
                    sbuf.append(strPattern, handledPosition, strPatternLength);
                    return sbuf.toString();
                }
            }
            else
            {
                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH)
                {
                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH)
                    {
                        // 转义符之前还有一个转义符,占位符依旧有效
                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
                        handledPosition = delimIndex + 2;
                    }
                    else
                    {
                        // 占位符被转义
                        argIndex--;
                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
                        sbuf.append(C_DELIM_START);
                        handledPosition = delimIndex + 1;
                    }
                }
                else
                {
                    // 正常占位符
                    sbuf.append(strPattern, handledPosition, delimIndex);
                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
                    handledPosition = delimIndex + 2;
                }
            }
        }
        // 加入最后一个占位符后所有的字符
        sbuf.append(strPattern, handledPosition, strPattern.length());
        return sbuf.toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/easyExcel/BigDecimalConverter.java
New file
@@ -0,0 +1,60 @@
package com.ruoyi.common.easyExcel;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalConverter implements Converter<BigDecimal> {
    @Override
    public Class<?> supportJavaTypeKey() {
        return String.class;
    }
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
    /**
     * 这里读的时候会调用
     *
     * @param context
     * @return
     */
    @Override
    public BigDecimal convertToJavaData(ReadConverterContext<?> context) {
        try {
            // 检查是否是字符串类型的单元格
            if (CellDataTypeEnum.STRING.equals(context.getReadCellData().getType())) {
                return new BigDecimal(context.getReadCellData().getStringValue()).setScale(10, RoundingMode.DOWN);
            }
            // 检查是否是数字类型的单元格
            else if (CellDataTypeEnum.NUMBER.equals(context.getReadCellData().getType())) {
                // 获取数字值并转化为字符串
                return new BigDecimal(String.valueOf(context.getReadCellData().getNumberValue())).setScale(10, RoundingMode.DOWN);
            } else {
                return null; // 返回null表示未能识别的单元格类型
            }
        }catch (Exception e){
            return null;
        }
    }
    /**
     * 这里是写的时候会调用 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<BigDecimal> context) {
        return new WriteCellData<>(String.valueOf(context.getValue()));
    }
}
pt-common/src/main/java/com/ruoyi/common/easyExcel/DateConverter.java
New file
@@ -0,0 +1,130 @@
package com.ruoyi.common.easyExcel;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.DateUtils;
import com.ruoyi.common.exception.GlobalException;
import org.apache.poi.ss.usermodel.DateUtil;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import java.util.List;
import java.util.Arrays;
public class DateConverter implements Converter<Date> {
    private static final List<String> DEFAULT_DATE_FORMATS = Arrays.asList(
            "yyyy/M/d","dd-M-yyyy", "M/d/yyyy", "yyyy-MM-dd","yyyy-MM",
            "yyyy.MM.dd","yyyy.MM","yyyy","yyyy年MM月dd日","yyyy年MM月","yyyy年"
    );
    @Override
    public Class<Date> supportJavaTypeKey() {
        return Date.class;
    }
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
    /**
     * 这里读的时候会调用
     *
     * @param cellData            excel数据 (NotNull)
     * @param contentProperty     excel属性 (Nullable)
     * @param globalConfiguration 全局配置 (NotNull)
     * @return 读取到内存中的数据
     */
    @Override
    public Date convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        try {
            String stringValue = "";
            // 1. Excel 单元格是数字类型(如 40969),代表日期
            if (cellData.getType() == CellDataTypeEnum.NUMBER) {
                //这里有可能是 2025年,是数字类型,也可能是2025.08也是数字,有可能是2025/08/01(这种excel读取是数字类型)
                //2000以后都是5位数
                if(cellData.getNumberValue().toString().contains(".")
                || cellData.getNumberValue().compareTo(new BigDecimal("2050")) <= 0){
                    stringValue = cellData.getNumberValue().toString();
                }else {
                    double numericDate = cellData.getNumberValue().doubleValue();
                    Date date = DateUtil.getJavaDate(numericDate, false); // HSSF 日期基准是 1900-01-01
//                    System.out.println(cn.hutool.core.date.DateUtil.format(date, "yyyy-MM-dd"));
                    return date;
                }
            }else {
                 stringValue = cellData.getStringValue();
            }
            try {
                Date parsedDate = parseDate(stringValue);
                if (parsedDate != null) {
//                    System.out.println(cn.hutool.core.date.DateUtil.format(parsedDate, "yyyy-MM-dd"));
                    return parsedDate;
                } else {
                    throw new ParseException("日期格式不匹配", 0);
                }
            } catch (Exception e) {
                throw new ParseException("日期格式不匹配", 0);
            }
        } catch (Exception e) {
            throw new GlobalException("日期格式错误,请保证时间格式为:yyyy/m/d, yyyy-MM-dd, dd-M-yyyy, M/d/yyyy");
        }
    }
    private Date parseDate(String dateStr) throws ParseException {
        for (String defaultFormat : DEFAULT_DATE_FORMATS) {
            SimpleDateFormat sdf = new SimpleDateFormat(defaultFormat, Locale.getDefault());
            sdf.setTimeZone(TimeZone.getDefault());
            try {
                return sdf.parse(dateStr);
            } catch (ParseException e) {
                // 继续尝试下一个格式
            }
        }
        return null;
    }
    public boolean isValidDate(Date date) {
        if (date == null) {
            return false;
        }
        // 定义日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        sdf.setLenient(false); // 关闭宽松模式,严格校验日期
        // 将 Date 转换成字符串,再转换回 Date,确保格式正确
        String dateStr = sdf.format(date);
        return cn.hutool.core.date.DateUtil.parse(dateStr) == null ? false:true;
    }
    /**
     * 写的时候会调用
     *
     * @param value               java value (NotNull)
     * @param contentProperty     excel属性 (Nullable)
     * @param globalConfiguration 全局配置 (NotNull)
     * @return 写出到excel文件的数据
     */
    @Override
    public WriteCellData<Date> convertToExcelData(Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        DateTimeFormat annotation = contentProperty.getField().getAnnotation(DateTimeFormat.class);
        String format = Objects.nonNull(annotation) ? annotation.pattern() : "yyyy-MM-dd";
        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
        sdf.setTimeZone(TimeZone.getDefault());
        String result = sdf.format(value);
        return new WriteCellData<>(result);
    }
}
pt-common/src/main/java/com/ruoyi/common/easyExcel/MultiDropdownWriteHandler.java
New file
@@ -0,0 +1,50 @@
package com.ruoyi.common.easyExcel;
import com.alibaba.excel.write.handler.AbstractSheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddressList;
import java.util.Map;
public class MultiDropdownWriteHandler extends AbstractSheetWriteHandler {
    // 使用 Map 存储列索引和对应的下拉选项
    private final Map<Integer, String[]> dropdownOptionsMap;
    public MultiDropdownWriteHandler(Map<Integer, String[]> dropdownOptionsMap) {
        this.dropdownOptionsMap = dropdownOptionsMap;
    }
    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        Sheet sheet = writeSheetHolder.getSheet();
        DataValidationHelper helper = sheet.getDataValidationHelper();
        for (Map.Entry<Integer, String[]> entry : dropdownOptionsMap.entrySet()) {
            int columnIndex = entry.getKey();
            String[] options = entry.getValue();
            // 定义下拉框应用的单元格范围(这里假设从第1行到第100行)
//            CellRangeAddressList addressList = new CellRangeAddressList(1, 100, columnIndex, columnIndex);
            CellRangeAddressList addressList = new CellRangeAddressList(0, 100, columnIndex, columnIndex);
            // 创建下拉选项
            DataValidationConstraint constraint = helper.createExplicitListConstraint(options);
            DataValidation dataValidation = helper.createValidation(constraint, addressList);
            // 设置校验
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
            // 将校验应用到 sheet
            sheet.addValidationData(dataValidation);
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/easyExcel/NumberConverter.java
New file
@@ -0,0 +1,57 @@
package com.ruoyi.common.easyExcel;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
public class NumberConverter implements Converter<Integer> {
    @Override
    public Class<?> supportJavaTypeKey() {
        return String.class;
    }
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
    /**
     * 这里读的时候会调用
     *
     * @param context
     * @return
     */
    @Override
    public Integer convertToJavaData(ReadConverterContext<?> context) {
        try {
            // 检查是否是字符串类型的单元格
            if (CellDataTypeEnum.STRING.equals(context.getReadCellData().getType())) {
                return Integer.parseInt(context.getReadCellData().getStringValue());
            }
            // 检查是否是数字类型的单元格
            else if (CellDataTypeEnum.NUMBER.equals(context.getReadCellData().getType())) {
                // 获取数字值并转化为字符串
                return context.getReadCellData().getNumberValue().intValue();
            } else {
                return null; // 返回null表示未能识别的单元格类型
            }
        }catch (Exception e){
            return null;
        }
    }
    /**
     * 这里是写的时候会调用 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) {
        return new WriteCellData<>(String.valueOf(context.getValue()));
    }
}
pt-common/src/main/java/com/ruoyi/common/easyExcel/StringConverter.java
New file
@@ -0,0 +1,62 @@
package com.ruoyi.common.easyExcel;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import java.math.BigDecimal;
import java.util.Objects;
public class StringConverter implements Converter<String> {
    @Override
    public Class<?> supportJavaTypeKey() {
        return String.class;
    }
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
    /**
     * 这里读的时候会调用
     *
     * @param context
     * @return
     */
    @Override
    public String convertToJavaData(ReadConverterContext<?> context) {
        try {
            String values = "";
            if (StrUtil.isNotBlank(context.getReadCellData().getStringValue())) {
                values = String.valueOf(context.getReadCellData().getStringValue());
            } else if (Objects.nonNull(context.getReadCellData().getNumberValue())) {
                BigDecimal test = context.getReadCellData().getNumberValue().setScale(0, BigDecimal.ROUND_DOWN);
                values = test.toString();
            }
            if (ObjUtil.isEmpty(values)) {
                return "";
            }
            return values.trim();
        }catch (Exception e){
            return "";
        }
    }
    /**
     * 这里是写的时候会调用 不用管
     *
     * @return
     */
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
        return new WriteCellData<>(String.valueOf(context.getValue()));
    }
}
pt-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.common.enums;
/**
 * 操作状态
 *
 * @author ruoyi
 *
 */
public enum BusinessStatus
{
    /**
     * 成功
     */
    SUCCESS,
    /**
     * 失败
     */
    FAIL,
}
pt-common/src/main/java/com/ruoyi/common/enums/BusinessType.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.common.enums;
/**
 * 业务操作类型
 *
 * @author ruoyi
 */
public enum BusinessType
{
    /**
     * 其它
     */
    OTHER,
    /**
     * 新增
     */
    INSERT,
    /**
     * 修改
     */
    UPDATE,
    /**
     * 删除
     */
    DELETE,
    /**
     * 授权
     */
    GRANT,
    /**
     * 导出
     */
    EXPORT,
    /**
     * 导入
     */
    IMPORT,
    /**
     * 强退
     */
    FORCE,
    /**
     * 生成代码
     */
    GENCODE,
    /**
     * 清空数据
     */
    CLEAN,
}
pt-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.common.enums;
/**
 * 数据源
 *
 * @author ruoyi
 */
public enum DataSourceType
{
    /**
     * 主库
     */
    MASTER,
    /**
     * 从库
     */
    SLAVE
}
pt-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.common.enums;
import java.util.function.Function;
import com.ruoyi.common.utils.DesensitizedUtil;
/**
 * 脱敏类型
 *
 * @author ruoyi
 */
public enum DesensitizedType
{
    /**
     * 姓名,第2位星号替换
     */
    USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
    /**
     * 密码,全部字符都用*代替
     */
    PASSWORD(DesensitizedUtil::password),
    /**
     * 身份证,中间10位星号替换
     */
    ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),
    /**
     * 手机号,中间4位星号替换
     */
    PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
    /**
     * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
     */
    EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
    /**
     * 银行卡号,保留最后4位,其他星号替换
     */
    BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
    /**
     * 车牌号码,包含普通车辆、新能源车辆
     */
    CAR_LICENSE(DesensitizedUtil::carLicense);
    private final Function<String, String> desensitizer;
    DesensitizedType(Function<String, String> desensitizer)
    {
        this.desensitizer = desensitizer;
    }
    public Function<String, String> desensitizer()
    {
        return desensitizer;
    }
}
pt-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.common.enums;
import java.util.HashMap;
import java.util.Map;
import org.springframework.lang.Nullable;
/**
 * 请求方式
 *
 * @author ruoyi
 */
public enum HttpMethod
{
    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
    private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
    static
    {
        for (HttpMethod httpMethod : values())
        {
            mappings.put(httpMethod.name(), httpMethod);
        }
    }
    @Nullable
    public static HttpMethod resolve(@Nullable String method)
    {
        return (method != null ? mappings.get(method) : null);
    }
    public boolean matches(String method)
    {
        return (this == resolve(method));
    }
}
pt-common/src/main/java/com/ruoyi/common/enums/LimitType.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.common.enums;
/**
 * 限流类型
 *
 * @author ruoyi
 */
public enum LimitType
{
    /**
     * 默认策略全局限流
     */
    DEFAULT,
    /**
     * 根据请求者IP进行限流
     */
    IP
}
pt-common/src/main/java/com/ruoyi/common/enums/OperatorType.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.common.enums;
/**
 * 操作人类别
 *
 * @author ruoyi
 */
public enum OperatorType
{
    /**
     * 其它
     */
    OTHER,
    /**
     * 后台用户
     */
    MANAGE,
    /**
     * 手机端用户
     */
    MOBILE
}
pt-common/src/main/java/com/ruoyi/common/enums/UserStatus.java
New file
@@ -0,0 +1,30 @@
package com.ruoyi.common.enums;
/**
 * 用户状态
 *
 * @author ruoyi
 */
public enum UserStatus
{
    OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除");
    private final String code;
    private final String info;
    UserStatus(String code, String info)
    {
        this.code = code;
        this.info = info;
    }
    public String getCode()
    {
        return code;
    }
    public String getInfo()
    {
        return info;
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/BaseException.java
New file
@@ -0,0 +1,15 @@
package com.ruoyi.common.exception;
/**
 * 业务异常
 */
public class BaseException extends RuntimeException {
    public BaseException() {
    }
    public BaseException(String msg) {
        super(msg);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java
New file
@@ -0,0 +1,15 @@
package com.ruoyi.common.exception;
/**
 * 演示模式异常
 *
 * @author ruoyi
 */
public class DemoModeException extends RuntimeException
{
    private static final long serialVersionUID = 1L;
    public DemoModeException()
    {
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/GlobalException.java
New file
@@ -0,0 +1,58 @@
package com.ruoyi.common.exception;
/**
 * 全局异常
 *
 * @author ruoyi
 */
public class GlobalException extends RuntimeException
{
    private static final long serialVersionUID = 1L;
    /**
     * 错误提示
     */
    private String message;
    /**
     * 错误明细,内部调试错误
     *
     * 和 {@link #getDetailMessage()} 一致的设计
     */
    private String detailMessage;
    /**
     * 空构造方法,避免反序列化问题
     */
    public GlobalException()
    {
    }
    public GlobalException(String message)
    {
        this.message = message;
    }
    public String getDetailMessage()
    {
        return detailMessage;
    }
    public GlobalException setDetailMessage(String detailMessage)
    {
        this.detailMessage = detailMessage;
        return this;
    }
    @Override
    public String getMessage()
    {
        return message;
    }
    public GlobalException setMessage(String message)
    {
        this.message = message;
        return this;
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/ServiceException.java
New file
@@ -0,0 +1,74 @@
package com.ruoyi.common.exception;
/**
 * 业务异常
 *
 * @author ruoyi
 */
public final class ServiceException extends RuntimeException
{
    private static final long serialVersionUID = 1L;
    /**
     * 错误码
     */
    private Integer code;
    /**
     * 错误提示
     */
    private String message;
    /**
     * 错误明细,内部调试错误
     *
     * 和 {@link CommonResult#getDetailMessage()} 一致的设计
     */
    private String detailMessage;
    /**
     * 空构造方法,避免反序列化问题
     */
    public ServiceException()
    {
    }
    public ServiceException(String message)
    {
        this.message = message;
    }
    public ServiceException(String message, Integer code)
    {
        this.message = message;
        this.code = code;
    }
    public String getDetailMessage()
    {
        return detailMessage;
    }
    @Override
    public String getMessage()
    {
        return message;
    }
    public Integer getCode()
    {
        return code;
    }
    public ServiceException setMessage(String message)
    {
        this.message = message;
        return this;
    }
    public ServiceException setDetailMessage(String detailMessage)
    {
        this.detailMessage = detailMessage;
        return this;
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/UtilException.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.common.exception;
/**
 * 工具类异常
 *
 * @author ruoyi
 */
public class UtilException extends RuntimeException
{
    private static final long serialVersionUID = 8247610319171014183L;
    public UtilException(Throwable e)
    {
        super(e.getMessage(), e);
    }
    public UtilException(String message)
    {
        super(message);
    }
    public UtilException(String message, Throwable throwable)
    {
        super(message, throwable);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java
New file
@@ -0,0 +1,97 @@
package com.ruoyi.common.exception.base;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
/**
 * 基础异常
 *
 * @author ruoyi
 */
public class BaseException extends RuntimeException
{
    private static final long serialVersionUID = 1L;
    /**
     * 所属模块
     */
    private String module;
    /**
     * 错误码
     */
    private String code;
    /**
     * 错误码对应的参数
     */
    private Object[] args;
    /**
     * 错误消息
     */
    private String defaultMessage;
    public BaseException(String module, String code, Object[] args, String defaultMessage)
    {
        this.module = module;
        this.code = code;
        this.args = args;
        this.defaultMessage = defaultMessage;
    }
    public BaseException(String module, String code, Object[] args)
    {
        this(module, code, args, null);
    }
    public BaseException(String module, String defaultMessage)
    {
        this(module, null, null, defaultMessage);
    }
    public BaseException(String code, Object[] args)
    {
        this(null, code, args, null);
    }
    public BaseException(String defaultMessage)
    {
        this(null, null, null, defaultMessage);
    }
    @Override
    public String getMessage()
    {
        String message = null;
        if (!StringUtils.isEmpty(code))
        {
            message = MessageUtils.message(code, args);
        }
        if (message == null)
        {
            message = defaultMessage;
        }
        return message;
    }
    public String getModule()
    {
        return module;
    }
    public String getCode()
    {
        return code;
    }
    public Object[] getArgs()
    {
        return args;
    }
    public String getDefaultMessage()
    {
        return defaultMessage;
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/file/FileException.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.common.exception.file;
import com.ruoyi.common.exception.base.BaseException;
/**
 * 文件信息异常类
 *
 * @author ruoyi
 */
public class FileException extends BaseException
{
    private static final long serialVersionUID = 1L;
    public FileException(String code, Object[] args)
    {
        super("file", code, args, null);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.file;
/**
 * 文件名称超长限制异常类
 *
 * @author ruoyi
 */
public class FileNameLengthLimitExceededException extends FileException
{
    private static final long serialVersionUID = 1L;
    public FileNameLengthLimitExceededException(int defaultFileNameLength)
    {
        super("upload.filename.exceed.length", new Object[] { defaultFileNameLength });
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.file;
/**
 * 文件名大小限制异常类
 *
 * @author ruoyi
 */
public class FileSizeLimitExceededException extends FileException
{
    private static final long serialVersionUID = 1L;
    public FileSizeLimitExceededException(long defaultMaxSize)
    {
        super("upload.exceed.maxSize", new Object[] { defaultMaxSize });
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java
New file
@@ -0,0 +1,61 @@
package com.ruoyi.common.exception.file;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
 * 文件上传异常类
 *
 * @author ruoyi
 */
public class FileUploadException extends Exception
{
    private static final long serialVersionUID = 1L;
    private final Throwable cause;
    public FileUploadException()
    {
        this(null, null);
    }
    public FileUploadException(final String msg)
    {
        this(msg, null);
    }
    public FileUploadException(String msg, Throwable cause)
    {
        super(msg);
        this.cause = cause;
    }
    @Override
    public void printStackTrace(PrintStream stream)
    {
        super.printStackTrace(stream);
        if (cause != null)
        {
            stream.println("Caused by:");
            cause.printStackTrace(stream);
        }
    }
    @Override
    public void printStackTrace(PrintWriter writer)
    {
        super.printStackTrace(writer);
        if (cause != null)
        {
            writer.println("Caused by:");
            cause.printStackTrace(writer);
        }
    }
    @Override
    public Throwable getCause()
    {
        return cause;
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java
New file
@@ -0,0 +1,80 @@
package com.ruoyi.common.exception.file;
import java.util.Arrays;
/**
 * 文件上传 误异常类
 *
 * @author ruoyi
 */
public class InvalidExtensionException extends FileUploadException
{
    private static final long serialVersionUID = 1L;
    private String[] allowedExtension;
    private String extension;
    private String filename;
    public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
    {
        super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式");
        this.allowedExtension = allowedExtension;
        this.extension = extension;
        this.filename = filename;
    }
    public String[] getAllowedExtension()
    {
        return allowedExtension;
    }
    public String getExtension()
    {
        return extension;
    }
    public String getFilename()
    {
        return filename;
    }
    public static class InvalidImageExtensionException extends InvalidExtensionException
    {
        private static final long serialVersionUID = 1L;
        public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename)
        {
            super(allowedExtension, extension, filename);
        }
    }
    public static class InvalidFlashExtensionException extends InvalidExtensionException
    {
        private static final long serialVersionUID = 1L;
        public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename)
        {
            super(allowedExtension, extension, filename);
        }
    }
    public static class InvalidMediaExtensionException extends InvalidExtensionException
    {
        private static final long serialVersionUID = 1L;
        public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename)
        {
            super(allowedExtension, extension, filename);
        }
    }
    public static class InvalidVideoExtensionException extends InvalidExtensionException
    {
        private static final long serialVersionUID = 1L;
        public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename)
        {
            super(allowedExtension, extension, filename);
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.common.exception.job;
/**
 * 计划策略异常
 *
 * @author ruoyi
 */
public class TaskException extends Exception
{
    private static final long serialVersionUID = 1L;
    private Code code;
    public TaskException(String msg, Code code)
    {
        this(msg, code, null);
    }
    public TaskException(String msg, Code code, Exception nestedEx)
    {
        super(msg, nestedEx);
        this.code = code;
    }
    public Code getCode()
    {
        return code;
    }
    public enum Code
    {
        TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
 * 黑名单IP异常类
 *
 * @author ruoyi
 */
public class BlackListException extends UserException
{
    private static final long serialVersionUID = 1L;
    public BlackListException()
    {
        super("login.blocked", null);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
 * 验证码错误异常类
 *
 * @author ruoyi
 */
public class CaptchaException extends UserException
{
    private static final long serialVersionUID = 1L;
    public CaptchaException()
    {
        super("user.jcaptcha.error", null);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
 * 验证码失效异常类
 *
 * @author ruoyi
 */
public class CaptchaExpireException extends UserException
{
    private static final long serialVersionUID = 1L;
    public CaptchaExpireException()
    {
        super("user.jcaptcha.expire", null);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/user/UserException.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.common.exception.user;
import com.ruoyi.common.exception.base.BaseException;
/**
 * 用户信息异常类
 *
 * @author ruoyi
 */
public class UserException extends BaseException
{
    private static final long serialVersionUID = 1L;
    public UserException(String code, Object[] args)
    {
        super("user", code, args, null);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
 * 用户不存在异常类
 *
 * @author ruoyi
 */
public class UserNotExistsException extends UserException
{
    private static final long serialVersionUID = 1L;
    public UserNotExistsException()
    {
        super("user.not.exists", null);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
 * 用户密码不正确或不符合规范异常类
 *
 * @author ruoyi
 */
public class UserPasswordNotMatchException extends UserException
{
    private static final long serialVersionUID = 1L;
    public UserPasswordNotMatchException()
    {
        super("user.password.not.match", null);
    }
}
pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.common.exception.user;
/**
 * 用户错误最大次数异常类
 *
 * @author ruoyi
 */
public class UserPasswordRetryLimitExceedException extends UserException
{
    private static final long serialVersionUID = 1L;
    public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime)
    {
        super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime });
    }
}
pt-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.common.filter;
import com.alibaba.fastjson2.filter.SimplePropertyPreFilter;
/**
 * 排除JSON敏感属性
 *
 * @author ruoyi
 */
public class PropertyPreExcludeFilter extends SimplePropertyPreFilter
{
    public PropertyPreExcludeFilter()
    {
    }
    public PropertyPreExcludeFilter addExcludes(String... filters)
    {
        for (int i = 0; i < filters.length; i++)
        {
            this.getExcludes().add(filters[i]);
        }
        return this;
    }
}
pt-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
New file
@@ -0,0 +1,52 @@
package com.ruoyi.common.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import com.ruoyi.common.utils.StringUtils;
/**
 * Repeatable 过滤器
 *
 * @author ruoyi
 */
public class RepeatableFilter implements Filter
{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest
                && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
        {
            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
        }
        if (null == requestWrapper)
        {
            chain.doFilter(request, response);
        }
        else
        {
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void destroy()
    {
    }
}
pt-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
New file
@@ -0,0 +1,76 @@
package com.ruoyi.common.filter;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.common.constant.Constants;
/**
 * 构建可重复读取inputStream的request
 *
 * @author ruoyi
 */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
{
    private final byte[] body;
    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
    {
        super(request);
        request.setCharacterEncoding(Constants.UTF8);
        response.setCharacterEncoding(Constants.UTF8);
        body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8);
    }
    @Override
    public BufferedReader getReader() throws IOException
    {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public ServletInputStream getInputStream() throws IOException
    {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream()
        {
            @Override
            public int read() throws IOException
            {
                return bais.read();
            }
            @Override
            public int available() throws IOException
            {
                return body.length;
            }
            @Override
            public boolean isFinished()
            {
                return false;
            }
            @Override
            public boolean isReady()
            {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener)
            {
            }
        };
    }
}
pt-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
New file
@@ -0,0 +1,75 @@
package com.ruoyi.common.filter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.enums.HttpMethod;
/**
 * 防止XSS攻击的过滤器
 *
 * @author ruoyi
 */
public class XssFilter implements Filter
{
    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        if (StringUtils.isNotEmpty(tempExcludes))
        {
            String[] urls = tempExcludes.split(",");
            for (String url : urls)
            {
                excludes.add(url);
            }
        }
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if (handleExcludeURL(req, resp))
        {
            chain.doFilter(request, response);
            return;
        }
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }
    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
    {
        String url = request.getServletPath();
        String method = request.getMethod();
        // GET DELETE 不过滤
        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method))
        {
            return true;
        }
        return StringUtils.matches(url, excludes);
    }
    @Override
    public void destroy()
    {
    }
}
pt-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
New file
@@ -0,0 +1,111 @@
package com.ruoyi.common.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.html.EscapeUtil;
/**
 * XSS过滤处理
 *
 * @author ruoyi
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
{
    /**
     * @param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request)
    {
        super(request);
    }
    @Override
    public String[] getParameterValues(String name)
    {
        String[] values = super.getParameterValues(name);
        if (values != null)
        {
            int length = values.length;
            String[] escapesValues = new String[length];
            for (int i = 0; i < length; i++)
            {
                // 防xss攻击和过滤前后空格
                escapesValues[i] = EscapeUtil.clean(values[i]).trim();
            }
            return escapesValues;
        }
        return super.getParameterValues(name);
    }
    @Override
    public ServletInputStream getInputStream() throws IOException
    {
        // 非json类型,直接返回
        if (!isJsonRequest())
        {
            return super.getInputStream();
        }
        // 为空,直接返回
        String json = IOUtils.toString(super.getInputStream(), "utf-8");
        if (StringUtils.isEmpty(json))
        {
            return super.getInputStream();
        }
        // xss过滤
        json = EscapeUtil.clean(json).trim();
        byte[] jsonBytes = json.getBytes("utf-8");
        final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes);
        return new ServletInputStream()
        {
            @Override
            public boolean isFinished()
            {
                return true;
            }
            @Override
            public boolean isReady()
            {
                return true;
            }
            @Override
            public int available() throws IOException
            {
                return jsonBytes.length;
            }
            @Override
            public void setReadListener(ReadListener readListener)
            {
            }
            @Override
            public int read() throws IOException
            {
                return bis.read();
            }
        };
    }
    /**
     * 是否是Json请求
     *
     * @param request
     */
    public boolean isJsonRequest()
    {
        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/Arith.java
New file
@@ -0,0 +1,113 @@
package com.ruoyi.common.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
 * 精确的浮点数运算
 *
 * @author ruoyi
 */
public class Arith
{
    /** 默认除法运算精度 */
    private static final int DEF_DIV_SCALE = 10;
    /** 这个类不能实例化 */
    private Arith()
    {
    }
    /**
     * 提供精确的加法运算。
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static double add(double v1, double v2)
    {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }
    /**
     * 提供精确的减法运算。
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(double v1, double v2)
    {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }
    /**
     * 提供精确的乘法运算。
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2)
    {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }
    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
     * 小数点以后10位,以后的数字四舍五入。
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static double div(double v1, double v2)
    {
        return div(v1, v2, DEF_DIV_SCALE);
    }
    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入。
     * @param v1 被除数
     * @param v2 除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(double v1, double v2, int scale)
    {
        if (scale < 0)
        {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        if (b1.compareTo(BigDecimal.ZERO) == 0)
        {
            return BigDecimal.ZERO.doubleValue();
        }
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }
    /**
     * 提供精确的小数位四舍五入处理。
     * @param v 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale)
    {
        if (scale < 0)
        {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.divide(BigDecimal.ONE, scale, RoundingMode.HALF_UP).doubleValue();
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
New file
@@ -0,0 +1,191 @@
package com.ruoyi.common.utils;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
/**
 * 时间工具类
 *
 * @author ruoyi
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{
    public static String YYYY = "yyyy";
    public static String YYYY_MM = "yyyy-MM";
    public static String YYYY_MM_DD = "yyyy-MM-dd";
    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate()
    {
        return new Date();
    }
    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     *
     * @return String
     */
    public static String getDate()
    {
        return dateTimeNow(YYYY_MM_DD);
    }
    public static final String getTime()
    {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }
    public static final String dateTimeNow()
    {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }
    public static final String dateTimeNow(final String format)
    {
        return parseDateToStr(format, new Date());
    }
    public static final String dateTime(final Date date)
    {
        return parseDateToStr(YYYY_MM_DD, date);
    }
    public static final String parseDateToStr(final String format, final Date date)
    {
        return new SimpleDateFormat(format).format(date);
    }
    public static final Date dateTime(final String format, final String ts)
    {
        try
        {
            return new SimpleDateFormat(format).parse(ts);
        }
        catch (ParseException e)
        {
            throw new RuntimeException(e);
        }
    }
    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static final String datePath()
    {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }
    /**
     * 日期路径 即年/月/日 如20180808
     */
    public static final String dateTime()
    {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }
    /**
     * 日期型字符串转化为日期 格式
     */
    public static Date parseDate(Object str)
    {
        if (str == null)
        {
            return null;
        }
        try
        {
            return parseDate(str.toString(), parsePatterns);
        }
        catch (ParseException e)
        {
            return null;
        }
    }
    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate()
    {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }
    /**
     * 计算相差天数
     */
    public static int differentDaysByMillisecond(Date date1, Date date2)
    {
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
    }
    /**
     * 计算时间差
     *
     * @param endDate 最后时间
     * @param startTime 开始时间
     * @return 时间差(天/小时/分钟)
     */
    public static String timeDistance(Date endDate, Date startTime)
    {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - startTime.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }
    /**
     * 增加 LocalDateTime ==> Date
     */
    public static Date toDate(LocalDateTime temporalAccessor)
    {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
    /**
     * 增加 LocalDate ==> Date
     */
    public static Date toDate(LocalDate temporalAccessor)
    {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java
New file
@@ -0,0 +1,49 @@
package com.ruoyi.common.utils;
/**
 * 脱敏工具类
 *
 * @author ruoyi
 */
public class DesensitizedUtil
{
    /**
     * 密码的全部字符都用*代替,比如:******
     *
     * @param password 密码
     * @return 脱敏后的密码
     */
    public static String password(String password)
    {
        if (StringUtils.isBlank(password))
        {
            return StringUtils.EMPTY;
        }
        return StringUtils.repeat('*', password.length());
    }
    /**
     * 车牌中间用*代替,如果是错误的车牌,不处理
     *
     * @param carLicense 完整的车牌号
     * @return 脱敏后的车牌
     */
    public static String carLicense(String carLicense)
    {
        if (StringUtils.isBlank(carLicense))
        {
            return StringUtils.EMPTY;
        }
        // 普通车牌
        if (carLicense.length() == 7)
        {
            carLicense = StringUtils.hide(carLicense, 3, 6);
        }
        else if (carLicense.length() == 8)
        {
            // 新能源车牌
            carLicense = StringUtils.hide(carLicense, 3, 7);
        }
        return carLicense;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
New file
@@ -0,0 +1,239 @@
package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.List;
import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
/**
 * 字典工具类
 *
 * @author ruoyi
 */
public class DictUtils
{
    /**
     * 分隔符
     */
    public static final String SEPARATOR = ",";
    /**
     * 设置字典缓存
     *
     * @param key 参数键
     * @param dictDatas 字典数据列表
     */
    public static void setDictCache(String key, List<SysDictData> dictDatas)
    {
        SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
    }
    /**
     * 获取字典缓存
     *
     * @param key 参数键
     * @return dictDatas 字典数据列表
     */
    public static List<SysDictData> getDictCache(String key)
    {
        JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
        if (StringUtils.isNotNull(arrayCache))
        {
            return arrayCache.toList(SysDictData.class);
        }
        return null;
    }
    /**
     * 根据字典类型和字典值获取字典标签
     *
     * @param dictType 字典类型
     * @param dictValue 字典值
     * @return 字典标签
     */
    public static String getDictLabel(String dictType, String dictValue)
    {
        if (StringUtils.isEmpty(dictValue))
        {
            return StringUtils.EMPTY;
        }
        return getDictLabel(dictType, dictValue, SEPARATOR);
    }
    /**
     * 根据字典类型和字典标签获取字典值
     *
     * @param dictType 字典类型
     * @param dictLabel 字典标签
     * @return 字典值
     */
    public static String getDictValue(String dictType, String dictLabel)
    {
        if (StringUtils.isEmpty(dictLabel))
        {
            return StringUtils.EMPTY;
        }
        return getDictValue(dictType, dictLabel, SEPARATOR);
    }
    /**
     * 根据字典类型和字典值获取字典标签
     *
     * @param dictType 字典类型
     * @param dictValue 字典值
     * @param separator 分隔符
     * @return 字典标签
     */
    public static String getDictLabel(String dictType, String dictValue, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StringUtils.isNull(datas))
        {
            return StringUtils.EMPTY;
        }
        if (StringUtils.containsAny(separator, dictValue))
        {
            for (SysDictData dict : datas)
            {
                for (String value : dictValue.split(separator))
                {
                    if (value.equals(dict.getDictValue()))
                    {
                        propertyString.append(dict.getDictLabel()).append(separator);
                        break;
                    }
                }
            }
        }
        else
        {
            for (SysDictData dict : datas)
            {
                if (dictValue.equals(dict.getDictValue()))
                {
                    return dict.getDictLabel();
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * 根据字典类型和字典标签获取字典值
     *
     * @param dictType 字典类型
     * @param dictLabel 字典标签
     * @param separator 分隔符
     * @return 字典值
     */
    public static String getDictValue(String dictType, String dictLabel, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StringUtils.isNull(datas))
        {
            return StringUtils.EMPTY;
        }
        if (StringUtils.containsAny(separator, dictLabel))
        {
            for (SysDictData dict : datas)
            {
                for (String label : dictLabel.split(separator))
                {
                    if (label.equals(dict.getDictLabel()))
                    {
                        propertyString.append(dict.getDictValue()).append(separator);
                        break;
                    }
                }
            }
        }
        else
        {
            for (SysDictData dict : datas)
            {
                if (dictLabel.equals(dict.getDictLabel()))
                {
                    return dict.getDictValue();
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * 根据字典类型获取字典所有值
     *
     * @param dictType 字典类型
     * @return 字典值
     */
    public static String getDictValues(String dictType)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StringUtils.isNull(datas))
        {
            return StringUtils.EMPTY;
        }
        for (SysDictData dict : datas)
        {
            propertyString.append(dict.getDictValue()).append(SEPARATOR);
        }
        return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
    }
    /**
     * 根据字典类型获取字典所有标签
     *
     * @param dictType 字典类型
     * @return 字典值
     */
    public static String getDictLabels(String dictType)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StringUtils.isNull(datas))
        {
            return StringUtils.EMPTY;
        }
        for (SysDictData dict : datas)
        {
            propertyString.append(dict.getDictLabel()).append(SEPARATOR);
        }
        return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
    }
    /**
     * 删除指定字典缓存
     *
     * @param key 字典键
     */
    public static void removeDictCache(String key)
    {
        SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
    }
    /**
     * 清空字典缓存
     */
    public static void clearDictCache()
    {
        Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*");
        SpringUtils.getBean(RedisCache.class).deleteObject(keys);
    }
    /**
     * 设置cache key
     *
     * @param configKey 参数键
     * @return 缓存键key
     */
    public static String getCacheKey(String configKey)
    {
        return CacheConstants.SYS_DICT_KEY + configKey;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java
New file
@@ -0,0 +1,39 @@
package com.ruoyi.common.utils;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.apache.commons.lang3.exception.ExceptionUtils;
/**
 * 错误信息处理类。
 *
 * @author ruoyi
 */
public class ExceptionUtil
{
    /**
     * 获取exception的详细错误信息。
     */
    public static String getExceptionMessage(Throwable e)
    {
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw, true));
        return sw.toString();
    }
    public static String getRootErrorMessage(Exception e)
    {
        Throwable root = ExceptionUtils.getRootCause(e);
        root = (root == null ? e : root);
        if (root == null)
        {
            return "";
        }
        String msg = root.getMessage();
        if (msg == null)
        {
            return "null";
        }
        return StringUtils.defaultString(msg);
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/LogUtils.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.common.utils;
/**
 * 处理并记录日志文件
 *
 * @author ruoyi
 */
public class LogUtils
{
    public static String getBlock(Object msg)
    {
        if (msg == null)
        {
            msg = "";
        }
        return "[" + msg.toString() + "]";
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.common.utils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import com.ruoyi.common.utils.spring.SpringUtils;
/**
 * 获取i18n资源文件
 *
 * @author ruoyi
 */
public class MessageUtils
{
    /**
     * 根据消息键和参数 获取消息 委托给spring messageSource
     *
     * @param code 消息键
     * @param args 参数
     * @return 获取国际化翻译值
     */
    public static String message(String code, Object... args)
    {
        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
New file
@@ -0,0 +1,35 @@
package com.ruoyi.common.utils;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.sql.SqlUtil;
/**
 * 分页工具类
 *
 * @author ruoyi
 */
public class PageUtils extends PageHelper
{
    /**
     * 设置请求分页数据
     */
    public static void startPage()
    {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
        Boolean reasonable = pageDomain.getReasonable();
        PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
    }
    /**
     * 清理分页的线程变量
     */
    public static void clearPage()
    {
        PageHelper.clearPage();
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
New file
@@ -0,0 +1,161 @@
package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import com.ruoyi.common.utils.sign.Md5Utils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.PatternMatchUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
/**
 * 安全服务工具类
 *
 * @author ruoyi
 */
public class SecurityUtils {
    /**
     * 用户ID
     **/
    public static Long getUserId() {
        try {
            return getLoginUser().getUserId();
        } catch (Exception e) {
            throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED);
        }
    }
    /**
     * 获取部门ID
     **/
    public static Long getDeptId() {
        try {
            return getLoginUser().getDeptId();
        } catch (Exception e) {
            throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
        }
    }
    /**
     * 获取用户账户
     **/
    public static String getUsername() {
        try {
            return getLoginUser().getUsername();
        } catch (Exception e) {
            throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
        }
    }
    /**
     * 获取用户
     **/
    public static LoginUser getLoginUser() {
        try {
            return (LoginUser) getAuthentication().getPrincipal();
        } catch (Exception e) {
            throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED);
        }
    }
    /**
     * 获取Authentication
     */
    public static Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
    /**
     * 生成BCryptPasswordEncoder密码
     *
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }
    /**
     * 判断密码是否相同
     *
     * @param rawPassword     真实密码
     * @param encodedPassword 加密后字符
     * @return 结果
     */
    public static boolean matchesPassword(String rawPassword, String encodedPassword) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }
    /**
     * 是否为管理员
     *
     * @param userId 用户ID
     * @return 结果
     */
    public static boolean isAdmin(Long userId) {
        return userId != null && 1L == userId;
    }
    /**
     * 验证用户是否具备某权限
     *
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public static boolean hasPermi(String permission) {
        return hasPermi(getLoginUser().getPermissions(), permission);
    }
    /**
     * 判断是否包含权限
     *
     * @param authorities 权限列表
     * @param permission  权限字符串
     * @return 用户是否具备某权限
     */
    public static boolean hasPermi(Collection<String> authorities, String permission) {
        return authorities.stream().filter(StringUtils::hasText)
                .anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission));
    }
    /**
     * 验证用户是否拥有某个角色
     *
     * @param role 角色标识
     * @return 用户是否具备某角色
     */
    public static boolean hasRole(String role) {
        List<SysRole> roleList = getLoginUser().getUser().getRoles();
        Collection<String> roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet());
        return hasRole(roles, role);
    }
    /**
     * 判断是否包含角色
     *
     * @param roles 角色列表
     * @param role  角色
     * @return 用户是否具备某角色权限
     */
    public static boolean hasRole(Collection<String> roles, String role) {
        return roles.stream().filter(StringUtils::hasText)
                .anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role));
    }
    public static void main(String[] args) {
        System.out.println(Md5Utils.hash("123456"));
        System.out.println(encryptPassword("e10adc3949ba59abbe56e057f20f883e"));
        System.out.println(matchesPassword("e10adc3949ba59abbe56e057f20f883e", "$2a$10$bWQjtUyAQ8NpAyvFupg0MO/gm1VtJnzs6fccCcqMxsLj1qhuJKv6K"));
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
New file
@@ -0,0 +1,218 @@
package com.ruoyi.common.utils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.text.Convert;
/**
 * 客户端工具类
 *
 * @author ruoyi
 */
public class ServletUtils
{
    /**
     * 获取String参数
     */
    public static String getParameter(String name)
    {
        return getRequest().getParameter(name);
    }
    /**
     * 获取String参数
     */
    public static String getParameter(String name, String defaultValue)
    {
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
    }
    /**
     * 获取Integer参数
     */
    public static Integer getParameterToInt(String name)
    {
        return Convert.toInt(getRequest().getParameter(name));
    }
    /**
     * 获取Integer参数
     */
    public static Integer getParameterToInt(String name, Integer defaultValue)
    {
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
    }
    /**
     * 获取Boolean参数
     */
    public static Boolean getParameterToBool(String name)
    {
        return Convert.toBool(getRequest().getParameter(name));
    }
    /**
     * 获取Boolean参数
     */
    public static Boolean getParameterToBool(String name, Boolean defaultValue)
    {
        return Convert.toBool(getRequest().getParameter(name), defaultValue);
    }
    /**
     * 获得所有请求参数
     *
     * @param request 请求对象{@link ServletRequest}
     * @return Map
     */
    public static Map<String, String[]> getParams(ServletRequest request)
    {
        final Map<String, String[]> map = request.getParameterMap();
        return Collections.unmodifiableMap(map);
    }
    /**
     * 获得所有请求参数
     *
     * @param request 请求对象{@link ServletRequest}
     * @return Map
     */
    public static Map<String, String> getParamMap(ServletRequest request)
    {
        Map<String, String> params = new HashMap<>();
        for (Map.Entry<String, String[]> entry : getParams(request).entrySet())
        {
            params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
        }
        return params;
    }
    /**
     * 获取request
     */
    public static HttpServletRequest getRequest()
    {
        return getRequestAttributes().getRequest();
    }
    /**
     * 获取response
     */
    public static HttpServletResponse getResponse()
    {
        return getRequestAttributes().getResponse();
    }
    /**
     * 获取session
     */
    public static HttpSession getSession()
    {
        return getRequest().getSession();
    }
    public static ServletRequestAttributes getRequestAttributes()
    {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
    /**
     * 将字符串渲染到客户端
     *
     * @param response 渲染对象
     * @param string 待渲染的字符串
     */
    public static void renderString(HttpServletResponse response, String string)
    {
        try
        {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
    /**
     * 是否是Ajax异步请求
     *
     * @param request
     */
    public static boolean isAjaxRequest(HttpServletRequest request)
    {
        String accept = request.getHeader("accept");
        if (accept != null && accept.contains("application/json"))
        {
            return true;
        }
        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest"))
        {
            return true;
        }
        String uri = request.getRequestURI();
        if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
        {
            return true;
        }
        String ajax = request.getParameter("__ajax");
        return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
    }
    /**
     * 内容编码
     *
     * @param str 内容
     * @return 编码后的内容
     */
    public static String urlEncode(String str)
    {
        try
        {
            return URLEncoder.encode(str, Constants.UTF8);
        }
        catch (UnsupportedEncodingException e)
        {
            return StringUtils.EMPTY;
        }
    }
    /**
     * 内容解码
     *
     * @param str 内容
     * @return 解码后的内容
     */
    public static String urlDecode(String str)
    {
        try
        {
            return URLDecoder.decode(str, Constants.UTF8);
        }
        catch (UnsupportedEncodingException e)
        {
            return StringUtils.EMPTY;
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
New file
@@ -0,0 +1,710 @@
package com.ruoyi.common.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.util.AntPathMatcher;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.text.StrFormatter;
/**
 * 字符串工具类
 *
 * @author ruoyi
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
    /** 空字符串 */
    private static final String NULLSTR = "";
    /** 下划线 */
    private static final char SEPARATOR = '_';
    /** 星号 */
    private static final char ASTERISK = '*';
    /**
     * 获取参数不为空值
     *
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue)
    {
        return value != null ? value : defaultValue;
    }
    /**
     * * 判断一个Collection是否为空, 包含List,Set,Queue
     *
     * @param coll 要判断的Collection
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Collection<?> coll)
    {
        return isNull(coll) || coll.isEmpty();
    }
    /**
     * * 判断一个Collection是否非空,包含List,Set,Queue
     *
     * @param coll 要判断的Collection
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Collection<?> coll)
    {
        return !isEmpty(coll);
    }
    /**
     * * 判断一个对象数组是否为空
     *
     * @param objects 要判断的对象数组
     ** @return true:为空 false:非空
     */
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }
    /**
     * * 判断一个对象数组是否非空
     *
     * @param objects 要判断的对象数组
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object[] objects)
    {
        return !isEmpty(objects);
    }
    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Map<?, ?> map)
    {
        return isNull(map) || map.isEmpty();
    }
    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Map<?, ?> map)
    {
        return !isEmpty(map);
    }
    /**
     * * 判断一个字符串是否为空串
     *
     * @param str String
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }
    /**
     * * 判断一个字符串是否为非空串
     *
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str)
    {
        return !isEmpty(str);
    }
    /**
     * * 判断一个对象是否为空
     *
     * @param object Object
     * @return true:为空 false:非空
     */
    public static boolean isNull(Object object)
    {
        return object == null;
    }
    /**
     * * 判断一个对象是否非空
     *
     * @param object Object
     * @return true:非空 false:空
     */
    public static boolean isNotNull(Object object)
    {
        return !isNull(object);
    }
    /**
     * * 判断一个对象是否是数组类型(Java基本型别的数组)
     *
     * @param object 对象
     * @return true:是数组 false:不是数组
     */
    public static boolean isArray(Object object)
    {
        return isNotNull(object) && object.getClass().isArray();
    }
    /**
     * 去空格
     */
    public static String trim(String str)
    {
        return (str == null ? "" : str.trim());
    }
    /**
     * 替换指定字符串的指定区间内字符为"*"
     *
     * @param str 字符串
     * @param startInclude 开始位置(包含)
     * @param endExclude 结束位置(不包含)
     * @return 替换后的字符串
     */
    public static String hide(CharSequence str, int startInclude, int endExclude)
    {
        if (isEmpty(str))
        {
            return NULLSTR;
        }
        final int strLength = str.length();
        if (startInclude > strLength)
        {
            return NULLSTR;
        }
        if (endExclude > strLength)
        {
            endExclude = strLength;
        }
        if (startInclude > endExclude)
        {
            // 如果起始位置大于结束位置,不替换
            return NULLSTR;
        }
        final char[] chars = new char[strLength];
        for (int i = 0; i < strLength; i++)
        {
            if (i >= startInclude && i < endExclude)
            {
                chars[i] = ASTERISK;
            }
            else
            {
                chars[i] = str.charAt(i);
            }
        }
        return new String(chars);
    }
    /**
     * 截取字符串
     *
     * @param str 字符串
     * @param start 开始
     * @return 结果
     */
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }
        if (start < 0)
        {
            start = str.length() + start;
        }
        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }
        return str.substring(start);
    }
    /**
     * 截取字符串
     *
     * @param str 字符串
     * @param start 开始
     * @param end 结束
     * @return 结果
     */
    public static String substring(final String str, int start, int end)
    {
        if (str == null)
        {
            return NULLSTR;
        }
        if (end < 0)
        {
            end = str.length() + end;
        }
        if (start < 0)
        {
            start = str.length() + start;
        }
        if (end > str.length())
        {
            end = str.length();
        }
        if (start > end)
        {
            return NULLSTR;
        }
        if (start < 0)
        {
            start = 0;
        }
        if (end < 0)
        {
            end = 0;
        }
        return str.substring(start, end);
    }
    /**
     * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串
     *
     * @param str 要截取的字符串
     * @param open 起始字符串
     * @param close 结束字符串
     * @return 截取结果
     */
    public static String substringBetweenLast(final String str, final String open, final String close)
    {
        if (isEmpty(str) || isEmpty(open) || isEmpty(close))
        {
            return NULLSTR;
        }
        final int start = str.indexOf(open);
        if (start != INDEX_NOT_FOUND)
        {
            final int end = str.lastIndexOf(close);
            if (end != INDEX_NOT_FOUND)
            {
                return str.substring(start + open.length(), end);
            }
        }
        return NULLSTR;
    }
    /**
     * 判断是否为空,并且不是空白字符
     *
     * @param str 要判断的value
     * @return 结果
     */
    public static boolean hasText(String str)
    {
        return (str != null && !str.isEmpty() && containsText(str));
    }
    private static boolean containsText(CharSequence str)
    {
        int strLen = str.length();
        for (int i = 0; i < strLen; i++)
        {
            if (!Character.isWhitespace(str.charAt(i)))
            {
                return true;
            }
        }
        return false;
    }
    /**
     * 格式化文本, {} 表示占位符<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例:<br>
     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param template 文本模板,被替换的部分用 {} 表示
     * @param params 参数值
     * @return 格式化后的文本
     */
    public static String format(String template, Object... params)
    {
        if (isEmpty(params) || isEmpty(template))
        {
            return template;
        }
        return StrFormatter.format(template, params);
    }
    /**
     * 是否为http(s)://开头
     *
     * @param link 链接
     * @return 结果
     */
    public static boolean ishttp(String link)
    {
        return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
    }
    /**
     * 字符串转set
     *
     * @param str 字符串
     * @param sep 分隔符
     * @return set集合
     */
    public static final Set<String> str2Set(String str, String sep)
    {
        return new HashSet<String>(str2List(str, sep, true, false));
    }
    /**
     * 字符串转list
     *
     * @param str 字符串
     * @param sep 分隔符
     * @param filterBlank 过滤纯空白
     * @param trim 去掉首尾空白
     * @return list集合
     */
    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
    {
        List<String> list = new ArrayList<String>();
        if (StringUtils.isEmpty(str))
        {
            return list;
        }
        // 过滤空白字符串
        if (filterBlank && StringUtils.isBlank(str))
        {
            return list;
        }
        String[] split = str.split(sep);
        for (String string : split)
        {
            if (filterBlank && StringUtils.isBlank(string))
            {
                continue;
            }
            if (trim)
            {
                string = string.trim();
            }
            list.add(string);
        }
        return list;
    }
    /**
     * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
     *
     * @param collection 给定的集合
     * @param array 给定的数组
     * @return boolean 结果
     */
    public static boolean containsAny(Collection<String> collection, String... array)
    {
        if (isEmpty(collection) || isEmpty(array))
        {
            return false;
        }
        else
        {
            for (String str : array)
            {
                if (collection.contains(str))
                {
                    return true;
                }
            }
            return false;
        }
    }
    /**
     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
     *
     * @param cs 指定字符串
     * @param searchCharSequences 需要检查的字符串数组
     * @return 是否包含任意一个字符串
     */
    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
    {
        if (isEmpty(cs) || isEmpty(searchCharSequences))
        {
            return false;
        }
        for (CharSequence testStr : searchCharSequences)
        {
            if (containsIgnoreCase(cs, testStr))
            {
                return true;
            }
        }
        return false;
    }
    /**
     * 驼峰转下划线命名
     */
    public static String toUnderScoreCase(String str)
    {
        if (str == null)
        {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大写
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);
            if (i > 0)
            {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            }
            else
            {
                preCharIsUpperCase = false;
            }
            curreCharIsUpperCase = Character.isUpperCase(c);
            if (i < (str.length() - 1))
            {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }
            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }
        return sb.toString();
    }
    /**
     * 是否包含字符串
     *
     * @param str 验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs)
    {
        if (str != null && strs != null)
        {
            for (String s : strs)
            {
                if (str.equalsIgnoreCase(trim(s)))
                {
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name)
    {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty())
        {
            // 没必要转换
            return "";
        }
        else if (!name.contains("_"))
        {
            // 不含下划线,仅将首字母大写
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels)
        {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty())
            {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }
    /**
     * 驼峰式命名法
     * 例如:user_name->userName
     */
    public static String toCamelCase(String s)
    {
        if (s == null)
        {
            return null;
        }
        if (s.indexOf(SEPARATOR) == -1)
        {
            return s;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);
            if (c == SEPARATOR)
            {
                upperCase = true;
            }
            else if (upperCase)
            {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            }
            else
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    /**
     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
     *
     * @param str 指定字符串
     * @param strs 需要检查的字符串数组
     * @return 是否匹配
     */
    public static boolean matches(String str, List<String> strs)
    {
        if (isEmpty(str) || isEmpty(strs))
        {
            return false;
        }
        for (String pattern : strs)
        {
            if (isMatch(pattern, str))
            {
                return true;
            }
        }
        return false;
    }
    /**
     * 判断url是否与规则配置:
     * ? 表示单个字符;
     * * 表示一层路径内的任意字符串,不可跨层级;
     * ** 表示任意层路径;
     *
     * @param pattern 匹配规则
     * @param url 需要匹配的url
     * @return
     */
    public static boolean isMatch(String pattern, String url)
    {
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(pattern, url);
    }
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj)
    {
        return (T) obj;
    }
    /**
     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
     *
     * @param num 数字对象
     * @param size 字符串指定长度
     * @return 返回数字的字符串格式,该字符串为指定长度。
     */
    public static final String padl(final Number num, final int size)
    {
        return padl(num.toString(), size, '0');
    }
    /**
     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
     *
     * @param s 原始字符串
     * @param size 字符串指定长度
     * @param c 用于补齐的字符
     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
     */
    public static final String padl(final String s, final int size, final char c)
    {
        final StringBuilder sb = new StringBuilder(size);
        if (s != null)
        {
            final int len = s.length();
            if (s.length() <= size)
            {
                for (int i = size - len; i > 0; i--)
                {
                    sb.append(c);
                }
                sb.append(s);
            }
            else
            {
                return s.substring(len - size, len);
            }
        }
        else
        {
            for (int i = size; i > 0; i--)
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/Threads.java
New file
@@ -0,0 +1,99 @@
package com.ruoyi.common.utils;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 线程相关工具类.
 *
 * @author ruoyi
 */
public class Threads
{
    private static final Logger logger = LoggerFactory.getLogger(Threads.class);
    /**
     * sleep等待,单位为毫秒
     */
    public static void sleep(long milliseconds)
    {
        try
        {
            Thread.sleep(milliseconds);
        }
        catch (InterruptedException e)
        {
            return;
        }
    }
    /**
     * 停止线程池
     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
     * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
     * 如果仍然超時,則強制退出.
     * 另对在shutdown时线程本身被调用中断做了处理.
     */
    public static void shutdownAndAwaitTermination(ExecutorService pool)
    {
        if (pool != null && !pool.isShutdown())
        {
            pool.shutdown();
            try
            {
                if (!pool.awaitTermination(120, TimeUnit.SECONDS))
                {
                    pool.shutdownNow();
                    if (!pool.awaitTermination(120, TimeUnit.SECONDS))
                    {
                        logger.info("Pool did not terminate");
                    }
                }
            }
            catch (InterruptedException ie)
            {
                pool.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }
    /**
     * 打印线程异常信息
     */
    public static void printException(Runnable r, Throwable t)
    {
        if (t == null && r instanceof Future<?>)
        {
            try
            {
                Future<?> future = (Future<?>) r;
                if (future.isDone())
                {
                    future.get();
                }
            }
            catch (CancellationException ce)
            {
                t = ce;
            }
            catch (ExecutionException ee)
            {
                t = ee.getCause();
            }
            catch (InterruptedException ie)
            {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null)
        {
            logger.error(t.getMessage(), t);
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java
New file
@@ -0,0 +1,110 @@
package com.ruoyi.common.utils.bean;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * Bean 工具类
 *
 * @author ruoyi
 */
public class BeanUtils extends org.springframework.beans.BeanUtils
{
    /** Bean方法名中属性名开始的下标 */
    private static final int BEAN_METHOD_PROP_INDEX = 3;
    /** * 匹配getter方法的正则表达式 */
    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
    /** * 匹配setter方法的正则表达式 */
    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
    /**
     * Bean属性复制工具方法。
     *
     * @param dest 目标对象
     * @param src 源对象
     */
    public static void copyBeanProp(Object dest, Object src)
    {
        try
        {
            copyProperties(src, dest);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    /**
     * 获取对象的setter方法。
     *
     * @param obj 对象
     * @return 对象的setter方法列表
     */
    public static List<Method> getSetterMethods(Object obj)
    {
        // setter方法列表
        List<Method> setterMethods = new ArrayList<Method>();
        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();
        // 查找setter方法
        for (Method method : methods)
        {
            Matcher m = SET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 1))
            {
                setterMethods.add(method);
            }
        }
        // 返回setter方法列表
        return setterMethods;
    }
    /**
     * 获取对象的getter方法。
     *
     * @param obj 对象
     * @return 对象的getter方法列表
     */
    public static List<Method> getGetterMethods(Object obj)
    {
        // getter方法列表
        List<Method> getterMethods = new ArrayList<Method>();
        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();
        // 查找getter方法
        for (Method method : methods)
        {
            Matcher m = GET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 0))
            {
                getterMethods.add(method);
            }
        }
        // 返回getter方法列表
        return getterMethods;
    }
    /**
     * 检查Bean方法名中的属性名是否相等。<br>
     * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
     *
     * @param m1 方法名1
     * @param m2 方法名2
     * @return 属性名一样返回true,否则返回false
     */
    public static boolean isMethodPropEquals(String m1, String m2)
    {
        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.common.utils.bean;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
/**
 * bean对象属性验证
 *
 * @author ruoyi
 */
public class BeanValidators
{
    public static void validateWithException(Validator validator, Object object, Class<?>... groups)
            throws ConstraintViolationException
    {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty())
        {
            throw new ConstraintViolationException(constraintViolations);
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java
New file
@@ -0,0 +1,76 @@
package com.ruoyi.common.utils.file;
import java.io.File;
import org.apache.commons.lang3.StringUtils;
/**
 * 文件类型工具类
 *
 * @author ruoyi
 */
public class FileTypeUtils
{
    /**
     * 获取文件类型
     * <p>
     * 例如: ruoyi.txt, 返回: txt
     *
     * @param file 文件名
     * @return 后缀(不含".")
     */
    public static String getFileType(File file)
    {
        if (null == file)
        {
            return StringUtils.EMPTY;
        }
        return getFileType(file.getName());
    }
    /**
     * 获取文件类型
     * <p>
     * 例如: ruoyi.txt, 返回: txt
     *
     * @param fileName 文件名
     * @return 后缀(不含".")
     */
    public static String getFileType(String fileName)
    {
        int separatorIndex = fileName.lastIndexOf(".");
        if (separatorIndex < 0)
        {
            return "";
        }
        return fileName.substring(separatorIndex + 1).toLowerCase();
    }
    /**
     * 获取文件类型
     *
     * @param photoByte 文件字节码
     * @return 后缀(不含".")
     */
    public static String getFileExtendName(byte[] photoByte)
    {
        String strFileExtendName = "JPG";
        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
        {
            strFileExtendName = "GIF";
        }
        else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
        {
            strFileExtendName = "JPG";
        }
        else if ((photoByte[0] == 66) && (photoByte[1] == 77))
        {
            strFileExtendName = "BMP";
        }
        else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
        {
            strFileExtendName = "PNG";
        }
        return strFileExtendName;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
New file
@@ -0,0 +1,232 @@
package com.ruoyi.common.utils.file;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.Seq;
/**
 * 文件上传工具类
 *
 * @author ruoyi
 */
public class FileUploadUtils
{
    /**
     * 默认大小 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
    /**
     * 默认的文件名最大长度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;
    /**
     * 默认上传的地址
     */
    private static String defaultBaseDir = RuoYiConfig.getProfile();
    public static void setDefaultBaseDir(String defaultBaseDir)
    {
        FileUploadUtils.defaultBaseDir = defaultBaseDir;
    }
    public static String getDefaultBaseDir()
    {
        return defaultBaseDir;
    }
    /**
     * 以默认配置进行文件上传
     *
     * @param file 上传的文件
     * @return 文件名称
     * @throws Exception
     */
    public static final String upload(MultipartFile file) throws IOException
    {
        try
        {
            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }
    /**
     * 根据文件路径上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @return 文件名称
     * @throws IOException
     */
    public static final String upload(String baseDir, MultipartFile file) throws IOException
    {
        try
        {
            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }
    /**
     * 文件上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException 比如读写文件出错时
     * @throws InvalidExtensionException 文件校验异常
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException
    {
        int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
        {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }
        assertAllowed(file, allowedExtension);
        String fileName = extractFilename(file);
        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
        file.transferTo(Paths.get(absPath));
        return getPathFileName(baseDir, fileName);
    }
    /**
     * 编码文件名
     */
    public static final String extractFilename(MultipartFile file)
    {
        return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
                FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
    }
    public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
    {
        File desc = new File(uploadDir + File.separator + fileName);
        if (!desc.exists())
        {
            if (!desc.getParentFile().exists())
            {
                desc.getParentFile().mkdirs();
            }
        }
        return desc;
    }
    public static final String getPathFileName(String uploadDir, String fileName) throws IOException
    {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
    }
    /**
     * 文件大小校验
     *
     * @param file 上传的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException
    {
        long size = file.getSize();
        if (size > DEFAULT_MAX_SIZE)
        {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }
        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
        {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
                        fileName);
            }
            else
            {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }
    }
    /**
     * 判断MIME类型是否是允许的MIME类型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
    {
        for (String str : allowedExtension)
        {
            if (str.equalsIgnoreCase(extension))
            {
                return true;
            }
        }
        return false;
    }
    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    public static final String getExtension(MultipartFile file)
    {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension))
        {
            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
        }
        return extension;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
New file
@@ -0,0 +1,367 @@
package com.ruoyi.common.utils.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.multipart.MultipartFile;
/**
 * 文件处理工具类
 *
 * @author ruoyi
 */
public class FileUtils
{
    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
    /**
     * 输出指定文件的byte数组
     *
     * @param filePath 文件路径
     * @param os 输出流
     * @return
     */
    public static void writeBytes(String filePath, OutputStream os) throws IOException
    {
        FileInputStream fis = null;
        try
        {
            File file = new File(filePath);
            if (!file.exists())
            {
                throw new FileNotFoundException(filePath);
            }
            fis = new FileInputStream(file);
            byte[] b = new byte[1024];
            int length;
            while ((length = fis.read(b)) > 0)
            {
                os.write(b, 0, length);
            }
        }
        catch (IOException e)
        {
            throw e;
        }
        finally
        {
            IOUtils.close(os);
            IOUtils.close(fis);
        }
    }
    public static void writeBytes(File file, OutputStream os) throws IOException
    {
        FileInputStream fis = null;
        try
        {
            if (!file.exists())
            {
                throw new FileNotFoundException(file.getPath());
            }
            fis = new FileInputStream(file);
            byte[] b = new byte[1024];
            int length;
            while ((length = fis.read(b)) > 0)
            {
                os.write(b, 0, length);
            }
        }
        catch (IOException e)
        {
            throw e;
        }
        finally
        {
            IOUtils.close(os);
            IOUtils.close(fis);
        }
    }
    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeImportBytes(byte[] data) throws IOException
    {
        return writeBytes(data, RuoYiConfig.getImportPath());
    }
    /**
     * 写数据到文件中
     *
     * @param data 数据
     * @param uploadDir 目标文件
     * @return 目标文件
     * @throws IOException IO异常
     */
    public static String writeBytes(byte[] data, String uploadDir) throws IOException
    {
        FileOutputStream fos = null;
        String pathName = "";
        try
        {
            String extension = getFileExtendName(data);
            pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
            File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
            fos = new FileOutputStream(file);
            fos.write(data);
        }
        finally
        {
            IOUtils.close(fos);
        }
        return FileUploadUtils.getPathFileName(uploadDir, pathName);
    }
    /**
     * 删除文件
     *
     * @param filePath 文件
     * @return
     */
    public static boolean deleteFile(String filePath)
    {
        boolean flag = false;
        File file = new File(filePath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists())
        {
            flag = file.delete();
        }
        return flag;
    }
    /**
     * 文件名称验证
     *
     * @param filename 文件名称
     * @return true 正常 false 非法
     */
    public static boolean isValidFilename(String filename)
    {
        return filename.matches(FILENAME_PATTERN);
    }
    /**
     * 检查文件是否可下载
     *
     * @param resource 需要下载的文件
     * @return true 正常 false 非法
     */
    public static boolean checkAllowDownload(String resource)
    {
        // 禁止目录上跳级别
        if (StringUtils.contains(resource, ".."))
        {
            return false;
        }
        // 检查允许下载的文件规则
        if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
        {
            return true;
        }
        // 不在允许下载的文件规则
        return false;
    }
    /**
     * 下载文件名重新编码
     *
     * @param request 请求对象
     * @param fileName 文件名
     * @return 编码后的文件名
     */
    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
    {
        final String agent = request.getHeader("USER-AGENT");
        String filename = fileName;
        if (agent.contains("MSIE"))
        {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        }
        else if (agent.contains("Firefox"))
        {
            // 火狐浏览器
            filename = new String(fileName.getBytes(), "ISO8859-1");
        }
        else if (agent.contains("Chrome"))
        {
            // google浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        else
        {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
    /**
     * 下载文件名重新编码
     *
     * @param response 响应对象
     * @param realFileName 真实文件名
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
    {
        String percentEncodedFileName = percentEncode(realFileName);
        StringBuilder contentDispositionValue = new StringBuilder();
        contentDispositionValue.append("attachment; filename=")
                .append(percentEncodedFileName)
                .append(";")
                .append("filename*=")
                .append("utf-8''")
                .append(percentEncodedFileName);
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
        response.setHeader("Content-disposition", contentDispositionValue.toString());
        response.setHeader("download-filename", percentEncodedFileName);
    }
    public static void setExcelResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
        // URL 编码文件名
        String percentEncodedFileName = java.net.URLEncoder.encode(realFileName, "UTF-8").replaceAll("\\+", "%20");
        // 设置 Content-Disposition 头
        String contentDisposition = "attachment; filename=\"" + percentEncodedFileName + "\"; filename*=utf-8''" + percentEncodedFileName;
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);
    }
    /**
     * 百分号编码工具方法
     *
     * @param s 需要百分号编码的字符串
     * @return 百分号编码后的字符串
     */
    public static String percentEncode(String s) throws UnsupportedEncodingException
    {
        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
        return encode.replaceAll("\\+", "%20");
    }
    /**
     * 获取图像后缀
     *
     * @param photoByte 图像数据
     * @return 后缀名
     */
    public static String getFileExtendName(byte[] photoByte)
    {
        String strFileExtendName = "jpg";
        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
        {
            strFileExtendName = "gif";
        }
        else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
        {
            strFileExtendName = "jpg";
        }
        else if ((photoByte[0] == 66) && (photoByte[1] == 77))
        {
            strFileExtendName = "bmp";
        }
        else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
        {
            strFileExtendName = "png";
        }
        return strFileExtendName;
    }
    /**
     * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
     *
     * @param fileName 路径名称
     * @return 没有文件路径的名称
     */
    public static String getName(String fileName)
    {
        if (fileName == null)
        {
            return null;
        }
        int lastUnixPos = fileName.lastIndexOf('/');
        int lastWindowsPos = fileName.lastIndexOf('\\');
        int index = Math.max(lastUnixPos, lastWindowsPos);
        return fileName.substring(index + 1);
    }
    /**
     * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
     *
     * @param fileName 路径名称
     * @return 没有文件路径和后缀的名称
     */
    public static String getNameNotSuffix(String fileName)
    {
        if (fileName == null)
        {
            return null;
        }
        String baseName = FilenameUtils.getBaseName(fileName);
        return baseName;
    }
    /**
     * 将 MultipartFile 转换为 File
     *
     * @param multipartFile 要转换的 MultipartFile 对象
     * @return 转换后的 File 对象
     * @throws IOException 如果文件处理过程中出现错误
     */
    public static File convertToFile(MultipartFile multipartFile) {
        File file;
        try {
            // 创建临时文件
            file = File.createTempFile("temp", multipartFile.getOriginalFilename());
            try (FileOutputStream fos = new FileOutputStream(file)) {
                // 将 MultipartFile 的内容写入到 File 中
                fos.write(multipartFile.getBytes());
            }
        }catch (IOException e){
            throw new GlobalException("读取文件失败");
        }
        // 返回 File 对象
        return file;
    }
    public static List<Map<Integer, String>> readExcelHead(File file) {
        // 构建读取器
        ExcelReaderSheetBuilder excelReaderSheetBuilder = EasyExcel.read(file)
                .sheet(0)
                .headRowNumber(0); // 设置表头的行数为1
        // 返回表头数据
        return excelReaderSheetBuilder.doReadSync();
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java
New file
@@ -0,0 +1,98 @@
package com.ruoyi.common.utils.file;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
/**
 * 图片处理工具类
 *
 * @author ruoyi
 */
public class ImageUtils
{
    private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
    public static byte[] getImage(String imagePath)
    {
        InputStream is = getFile(imagePath);
        try
        {
            return IOUtils.toByteArray(is);
        }
        catch (Exception e)
        {
            log.error("图片加载异常 {}", e);
            return null;
        }
        finally
        {
            IOUtils.closeQuietly(is);
        }
    }
    public static InputStream getFile(String imagePath)
    {
        try
        {
            byte[] result = readFile(imagePath);
            result = Arrays.copyOf(result, result.length);
            return new ByteArrayInputStream(result);
        }
        catch (Exception e)
        {
            log.error("获取图片异常 {}", e);
        }
        return null;
    }
    /**
     * 读取文件为字节数据
     *
     * @param url 地址
     * @return 字节数据
     */
    public static byte[] readFile(String url)
    {
        InputStream in = null;
        try
        {
            if (url.startsWith("http"))
            {
                // 网络地址
                URL urlObj = new URL(url);
                URLConnection urlConnection = urlObj.openConnection();
                urlConnection.setConnectTimeout(30 * 1000);
                urlConnection.setReadTimeout(60 * 1000);
                urlConnection.setDoInput(true);
                in = urlConnection.getInputStream();
            }
            else
            {
                // 本机地址
                String localPath = RuoYiConfig.getProfile();
                String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
                in = new FileInputStream(downloadPath);
            }
            return IOUtils.toByteArray(in);
        }
        catch (Exception e)
        {
            log.error("获取文件路径异常 {}", e);
            return null;
        }
        finally
        {
            IOUtils.closeQuietly(in);
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.common.utils.file;
/**
 * 媒体类型工具类
 *
 * @author ruoyi
 */
public class MimeTypeUtils
{
    public static final String IMAGE_PNG = "image/png";
    public static final String IMAGE_JPG = "image/jpg";
    public static final String IMAGE_JPEG = "image/jpeg";
    public static final String IMAGE_BMP = "image/bmp";
    public static final String IMAGE_GIF = "image/gif";
    public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
    public static final String[] FLASH_EXTENSION = { "swf", "flv" };
    public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
            "asf", "rm", "rmvb" };
    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
            // 图片
            "bmp", "gif", "jpg", "jpeg", "png",
            // word excel powerpoint
            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
            // 压缩文件
            "rar", "zip", "gz", "bz2",
            // 视频格式
            "mp4", "avi", "rmvb",
            // pdf
            "pdf" };
    public static String getExtension(String prefix)
    {
        switch (prefix)
        {
            case IMAGE_PNG:
                return "png";
            case IMAGE_JPG:
                return "jpg";
            case IMAGE_JPEG:
                return "jpeg";
            case IMAGE_BMP:
                return "bmp";
            case IMAGE_GIF:
                return "gif";
            default:
                return "";
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java
New file
@@ -0,0 +1,167 @@
package com.ruoyi.common.utils.html;
import com.ruoyi.common.utils.StringUtils;
/**
 * 转义和反转义工具类
 *
 * @author ruoyi
 */
public class EscapeUtil
{
    public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
    private static final char[][] TEXT = new char[64][];
    static
    {
        for (int i = 0; i < 64; i++)
        {
            TEXT[i] = new char[] { (char) i };
        }
        // special HTML characters
        TEXT['\''] = "&#039;".toCharArray(); // 单引号
        TEXT['"'] = "&#34;".toCharArray(); // 双引号
        TEXT['&'] = "&#38;".toCharArray(); // &符
        TEXT['<'] = "&#60;".toCharArray(); // 小于号
        TEXT['>'] = "&#62;".toCharArray(); // 大于号
    }
    /**
     * 转义文本中的HTML字符为安全的字符
     *
     * @param text 被转义的文本
     * @return 转义后的文本
     */
    public static String escape(String text)
    {
        return encode(text);
    }
    /**
     * 还原被转义的HTML特殊字符
     *
     * @param content 包含转义符的HTML内容
     * @return 转换后的字符串
     */
    public static String unescape(String content)
    {
        return decode(content);
    }
    /**
     * 清除所有HTML标签,但是不删除标签内的内容
     *
     * @param content 文本
     * @return 清除标签后的文本
     */
    public static String clean(String content)
    {
        return new HTMLFilter().filter(content);
    }
    /**
     * Escape编码
     *
     * @param text 被编码的文本
     * @return 编码后的字符
     */
    private static String encode(String text)
    {
        if (StringUtils.isEmpty(text))
        {
            return StringUtils.EMPTY;
        }
        final StringBuilder tmp = new StringBuilder(text.length() * 6);
        char c;
        for (int i = 0; i < text.length(); i++)
        {
            c = text.charAt(i);
            if (c < 256)
            {
                tmp.append("%");
                if (c < 16)
                {
                    tmp.append("0");
                }
                tmp.append(Integer.toString(c, 16));
            }
            else
            {
                tmp.append("%u");
                if (c <= 0xfff)
                {
                    // issue#I49JU8@Gitee
                    tmp.append("0");
                }
                tmp.append(Integer.toString(c, 16));
            }
        }
        return tmp.toString();
    }
    /**
     * Escape解码
     *
     * @param content 被转义的内容
     * @return 解码后的字符串
     */
    public static String decode(String content)
    {
        if (StringUtils.isEmpty(content))
        {
            return content;
        }
        StringBuilder tmp = new StringBuilder(content.length());
        int lastPos = 0, pos = 0;
        char ch;
        while (lastPos < content.length())
        {
            pos = content.indexOf("%", lastPos);
            if (pos == lastPos)
            {
                if (content.charAt(pos + 1) == 'u')
                {
                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
                    tmp.append(ch);
                    lastPos = pos + 6;
                }
                else
                {
                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
                    tmp.append(ch);
                    lastPos = pos + 3;
                }
            }
            else
            {
                if (pos == -1)
                {
                    tmp.append(content.substring(lastPos));
                    lastPos = content.length();
                }
                else
                {
                    tmp.append(content.substring(lastPos, pos));
                    lastPos = pos;
                }
            }
        }
        return tmp.toString();
    }
    public static void main(String[] args)
    {
        String html = "<script>alert(1);</script>";
        String escape = EscapeUtil.escape(html);
        // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
        // String html = "<123";
        // String html = "123>";
        System.out.println("clean: " + EscapeUtil.clean(html));
        System.out.println("escape: " + escape);
        System.out.println("unescape: " + EscapeUtil.unescape(escape));
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java
New file
@@ -0,0 +1,570 @@
package com.ruoyi.common.utils.html;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * HTML过滤器,用于去除XSS漏洞隐患。
 *
 * @author ruoyi
 */
public final class HTMLFilter
{
    /**
     * regex flag union representing /si modifiers in php
     **/
    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
    private static final Pattern P_END_ARROW = Pattern.compile("^>");
    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
    private static final Pattern P_AMP = Pattern.compile("&");
    private static final Pattern P_QUOTE = Pattern.compile("\"");
    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
    // @xxx could grow large... maybe use sesat's ReferenceMap
    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
    /**
     * set of allowed html elements, along with allowed attributes for each element
     **/
    private final Map<String, List<String>> vAllowed;
    /**
     * counts of open tags for each (allowable) html element
     **/
    private final Map<String, Integer> vTagCounts = new HashMap<>();
    /**
     * html elements which must always be self-closing (e.g. "<img />")
     **/
    private final String[] vSelfClosingTags;
    /**
     * html elements which must always have separate opening and closing tags (e.g. "<b></b>")
     **/
    private final String[] vNeedClosingTags;
    /**
     * set of disallowed html elements
     **/
    private final String[] vDisallowed;
    /**
     * attributes which should be checked for valid protocols
     **/
    private final String[] vProtocolAtts;
    /**
     * allowed protocols
     **/
    private final String[] vAllowedProtocols;
    /**
     * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
     **/
    private final String[] vRemoveBlanks;
    /**
     * entities allowed within html markup
     **/
    private final String[] vAllowedEntities;
    /**
     * flag determining whether comments are allowed in input String.
     */
    private final boolean stripComment;
    private final boolean encodeQuotes;
    /**
     * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
     * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
     */
    private final boolean alwaysMakeTags;
    /**
     * Default constructor.
     */
    public HTMLFilter()
    {
        vAllowed = new HashMap<>();
        final ArrayList<String> a_atts = new ArrayList<>();
        a_atts.add("href");
        a_atts.add("target");
        vAllowed.put("a", a_atts);
        final ArrayList<String> img_atts = new ArrayList<>();
        img_atts.add("src");
        img_atts.add("width");
        img_atts.add("height");
        img_atts.add("alt");
        vAllowed.put("img", img_atts);
        final ArrayList<String> no_atts = new ArrayList<>();
        vAllowed.put("b", no_atts);
        vAllowed.put("strong", no_atts);
        vAllowed.put("i", no_atts);
        vAllowed.put("em", no_atts);
        vSelfClosingTags = new String[] { "img" };
        vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
        vDisallowed = new String[] {};
        vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
        vProtocolAtts = new String[] { "src", "href" };
        vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
        vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
        stripComment = true;
        encodeQuotes = true;
        alwaysMakeTags = false;
    }
    /**
     * Map-parameter configurable constructor.
     *
     * @param conf map containing configuration. keys match field names.
     */
    @SuppressWarnings("unchecked")
    public HTMLFilter(final Map<String, Object> conf)
    {
        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
        vDisallowed = (String[]) conf.get("vDisallowed");
        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
        stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
    }
    private void reset()
    {
        vTagCounts.clear();
    }
    // ---------------------------------------------------------------
    // my versions of some PHP library functions
    public static String chr(final int decimal)
    {
        return String.valueOf((char) decimal);
    }
    public static String htmlSpecialChars(final String s)
    {
        String result = s;
        result = regexReplace(P_AMP, "&amp;", result);
        result = regexReplace(P_QUOTE, "&quot;", result);
        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
        return result;
    }
    // ---------------------------------------------------------------
    /**
     * given a user submitted input String, filter out any invalid or restricted html.
     *
     * @param input text (i.e. submitted by a user) than may contain html
     * @return "clean" version of input, with only valid, whitelisted html elements allowed
     */
    public String filter(final String input)
    {
        reset();
        String s = input;
        s = escapeComments(s);
        s = balanceHTML(s);
        s = checkTags(s);
        s = processRemoveBlanks(s);
        // s = validateEntities(s);
        return s;
    }
    public boolean isAlwaysMakeTags()
    {
        return alwaysMakeTags;
    }
    public boolean isStripComments()
    {
        return stripComment;
    }
    private String escapeComments(final String s)
    {
        final Matcher m = P_COMMENTS.matcher(s);
        final StringBuffer buf = new StringBuffer();
        if (m.find())
        {
            final String match = m.group(1); // (.*?)
            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
        }
        m.appendTail(buf);
        return buf.toString();
    }
    private String balanceHTML(String s)
    {
        if (alwaysMakeTags)
        {
            //
            // try and form html
            //
            s = regexReplace(P_END_ARROW, "", s);
            // 不追加结束标签
            s = regexReplace(P_BODY_TO_END, "<$1>", s);
            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
        }
        else
        {
            //
            // escape stray brackets
            //
            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
            //
            // the last regexp causes '<>' entities to appear
            // (we need to do a lookahead assertion so that the last bracket can
            // be used in the next pass of the regexp)
            //
            s = regexReplace(P_BOTH_ARROWS, "", s);
        }
        return s;
    }
    private String checkTags(String s)
    {
        Matcher m = P_TAGS.matcher(s);
        final StringBuffer buf = new StringBuffer();
        while (m.find())
        {
            String replaceStr = m.group(1);
            replaceStr = processTag(replaceStr);
            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
        }
        m.appendTail(buf);
        // these get tallied in processTag
        // (remember to reset before subsequent calls to filter method)
        final StringBuilder sBuilder = new StringBuilder(buf.toString());
        for (String key : vTagCounts.keySet())
        {
            for (int ii = 0; ii < vTagCounts.get(key); ii++)
            {
                sBuilder.append("</").append(key).append(">");
            }
        }
        s = sBuilder.toString();
        return s;
    }
    private String processRemoveBlanks(final String s)
    {
        String result = s;
        for (String tag : vRemoveBlanks)
        {
            if (!P_REMOVE_PAIR_BLANKS.containsKey(tag))
            {
                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
            }
            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
            if (!P_REMOVE_SELF_BLANKS.containsKey(tag))
            {
                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
            }
            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
        }
        return result;
    }
    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
    {
        Matcher m = regex_pattern.matcher(s);
        return m.replaceAll(replacement);
    }
    private String processTag(final String s)
    {
        // ending tags
        Matcher m = P_END_TAG.matcher(s);
        if (m.find())
        {
            final String name = m.group(1).toLowerCase();
            if (allowed(name))
            {
                if (!inArray(name, vSelfClosingTags))
                {
                    if (vTagCounts.containsKey(name))
                    {
                        vTagCounts.put(name, vTagCounts.get(name) - 1);
                        return "</" + name + ">";
                    }
                }
            }
        }
        // starting tags
        m = P_START_TAG.matcher(s);
        if (m.find())
        {
            final String name = m.group(1).toLowerCase();
            final String body = m.group(2);
            String ending = m.group(3);
            // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
            if (allowed(name))
            {
                final StringBuilder params = new StringBuilder();
                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
                final List<String> paramNames = new ArrayList<>();
                final List<String> paramValues = new ArrayList<>();
                while (m2.find())
                {
                    paramNames.add(m2.group(1)); // ([a-z0-9]+)
                    paramValues.add(m2.group(3)); // (.*?)
                }
                while (m3.find())
                {
                    paramNames.add(m3.group(1)); // ([a-z0-9]+)
                    paramValues.add(m3.group(3)); // ([^\"\\s']+)
                }
                String paramName, paramValue;
                for (int ii = 0; ii < paramNames.size(); ii++)
                {
                    paramName = paramNames.get(ii).toLowerCase();
                    paramValue = paramValues.get(ii);
                    // debug( "paramName='" + paramName + "'" );
                    // debug( "paramValue='" + paramValue + "'" );
                    // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
                    if (allowedAttribute(name, paramName))
                    {
                        if (inArray(paramName, vProtocolAtts))
                        {
                            paramValue = processParamProtocol(paramValue);
                        }
                        params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
                    }
                }
                if (inArray(name, vSelfClosingTags))
                {
                    ending = " /";
                }
                if (inArray(name, vNeedClosingTags))
                {
                    ending = "";
                }
                if (ending == null || ending.length() < 1)
                {
                    if (vTagCounts.containsKey(name))
                    {
                        vTagCounts.put(name, vTagCounts.get(name) + 1);
                    }
                    else
                    {
                        vTagCounts.put(name, 1);
                    }
                }
                else
                {
                    ending = " /";
                }
                return "<" + name + params + ending + ">";
            }
            else
            {
                return "";
            }
        }
        // comments
        m = P_COMMENT.matcher(s);
        if (!stripComment && m.find())
        {
            return "<" + m.group() + ">";
        }
        return "";
    }
    private String processParamProtocol(String s)
    {
        s = decodeEntities(s);
        final Matcher m = P_PROTOCOL.matcher(s);
        if (m.find())
        {
            final String protocol = m.group(1);
            if (!inArray(protocol, vAllowedProtocols))
            {
                // bad protocol, turn into local anchor link instead
                s = "#" + s.substring(protocol.length() + 1);
                if (s.startsWith("#//"))
                {
                    s = "#" + s.substring(3);
                }
            }
        }
        return s;
    }
    private String decodeEntities(String s)
    {
        StringBuffer buf = new StringBuffer();
        Matcher m = P_ENTITY.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.decode(match).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
        buf = new StringBuffer();
        m = P_ENTITY_UNICODE.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
        buf = new StringBuffer();
        m = P_ENCODE.matcher(s);
        while (m.find())
        {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16).intValue();
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();
        s = validateEntities(s);
        return s;
    }
    private String validateEntities(final String s)
    {
        StringBuffer buf = new StringBuffer();
        // validate entities throughout the string
        Matcher m = P_VALID_ENTITIES.matcher(s);
        while (m.find())
        {
            final String one = m.group(1); // ([^&;]*)
            final String two = m.group(2); // (?=(;|&|$))
            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
        }
        m.appendTail(buf);
        return encodeQuotes(buf.toString());
    }
    private String encodeQuotes(final String s)
    {
        if (encodeQuotes)
        {
            StringBuffer buf = new StringBuffer();
            Matcher m = P_VALID_QUOTES.matcher(s);
            while (m.find())
            {
                final String one = m.group(1); // (>|^)
                final String two = m.group(2); // ([^<]+?)
                final String three = m.group(3); // (<|$)
                // 不替换双引号为&quot;,防止json格式无效 regexReplace(P_QUOTE, "&quot;", two)
                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
            }
            m.appendTail(buf);
            return buf.toString();
        }
        else
        {
            return s;
        }
    }
    private String checkEntity(final String preamble, final String term)
    {
        return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&amp;" + preamble;
    }
    private boolean isValidEntity(final String entity)
    {
        return inArray(entity, vAllowedEntities);
    }
    private static boolean inArray(final String s, final String[] array)
    {
        for (String item : array)
        {
            if (item != null && item.equals(s))
            {
                return true;
            }
        }
        return false;
    }
    private boolean allowed(final String name)
    {
        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
    }
    private boolean allowedAttribute(final String name, final String paramName)
    {
        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
New file
@@ -0,0 +1,55 @@
package com.ruoyi.common.utils.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 通用http工具封装
 *
 * @author ruoyi
 */
public class HttpHelper
{
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
    public static String getBodyString(ServletRequest request)
    {
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try (InputStream inputStream = request.getInputStream())
        {
            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line = "";
            while ((line = reader.readLine()) != null)
            {
                sb.append(line);
            }
        }
        catch (IOException e)
        {
            LOGGER.warn("getBodyString出现问题!");
        }
        finally
        {
            if (reader != null)
            {
                try
                {
                    reader.close();
                }
                catch (IOException e)
                {
                    LOGGER.error(ExceptionUtils.getMessage(e));
                }
            }
        }
        return sb.toString();
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
New file
@@ -0,0 +1,293 @@
package com.ruoyi.common.utils.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.http.MediaType;
/**
 * 通用http发送方法
 *
 * @author ruoyi
 */
public class HttpUtils
{
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url)
    {
        return sendGet(url, StringUtils.EMPTY);
    }
    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param)
    {
        return sendGet(url, param, Constants.UTF8);
    }
    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String contentType)
    {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try
        {
            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
            }
            catch (Exception ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }
    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param)
    {
        return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
    }
    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @param param 请求参数
     * @param contentType 内容类型
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param, String contentType)
    {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try
        {
            log.info("sendPost - {}", url);
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("Content-Type", contentType);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (out != null)
                {
                    out.close();
                }
                if (in != null)
                {
                    in.close();
                }
            }
            catch (IOException ex)
            {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }
    public static String sendSSLPost(String url, String param)
    {
        return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
    }
    public static String sendSSLPost(String url, String param, String contentType)
    {
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try
        {
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("Content-Type", contentType);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null)
            {
                if (ret != null && !"".equals(ret.trim()))
                {
                    result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        }
        catch (ConnectException e)
        {
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }
    private static class TrustAnyTrustManager implements X509TrustManager
    {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
        {
        }
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
        {
        }
        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[] {};
        }
    }
    private static class TrustAnyHostnameVerifier implements HostnameVerifier
    {
        @Override
        public boolean verify(String hostname, SSLSession session)
        {
            return true;
        }
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
New file
@@ -0,0 +1,56 @@
package com.ruoyi.common.utils.ip;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpUtils;
/**
 * 获取地址类
 *
 * @author ruoyi
 */
public class AddressUtils
{
    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
    // IP地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
    // 未知地址
    public static final String UNKNOWN = "XX XX";
    public static String getRealAddressByIP(String ip)
    {
        // 内网不查询
        if (IpUtils.internalIp(ip))
        {
            return "内网IP";
        }
        if (RuoYiConfig.isAddressEnabled())
        {
            try
            {
                String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
                if (StringUtils.isEmpty(rspStr))
                {
                    log.error("获取地理位置异常 {}", ip);
                    return UNKNOWN;
                }
                JSONObject obj = JSON.parseObject(rspStr);
                String region = obj.getString("pro");
                String city = obj.getString("city");
                return String.format("%s %s", region, city);
            }
            catch (Exception e)
            {
                log.error("获取地理位置异常 {}", ip);
            }
        }
        return UNKNOWN;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
New file
@@ -0,0 +1,382 @@
package com.ruoyi.common.utils.ip;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
/**
 * 获取IP方法
 *
 * @author ruoyi
 */
public class IpUtils
{
    public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
    // 匹配 ip
    public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
    public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
    // 匹配网段
    public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
    /**
     * 获取客户端IP
     *
     * @return IP地址
     */
    public static String getIpAddr()
    {
        return getIpAddr(ServletUtils.getRequest());
    }
    /**
     * 获取客户端IP
     *
     * @param request 请求对象
     * @return IP地址
     */
    public static String getIpAddr(HttpServletRequest request)
    {
        if (request == null)
        {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
    }
    /**
     * 检查是否为内部IP地址
     *
     * @param ip IP地址
     * @return 结果
     */
    public static boolean internalIp(String ip)
    {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }
    /**
     * 检查是否为内部IP地址
     *
     * @param addr byte地址
     * @return 结果
     */
    private static boolean internalIp(byte[] addr)
    {
        if (StringUtils.isNull(addr) || addr.length < 2)
        {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0)
        {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4)
                {
                    return true;
                }
            case SECTION_5:
                switch (b1)
                {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }
    /**
     * 将IPv4地址转换成字节
     *
     * @param text IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(String text)
    {
        if (text.length() == 0)
        {
            return null;
        }
        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try
        {
            long l;
            int i;
            switch (elements.length)
            {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L))
                    {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L))
                    {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L))
                    {
                        return null;
                    }
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                        {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L))
                    {
                        return null;
                    }
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                        {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        }
        catch (NumberFormatException e)
        {
            return null;
        }
        return bytes;
    }
    /**
     * 获取IP地址
     *
     * @return 本地IP地址
     */
    public static String getHostIp()
    {
        try
        {
            return InetAddress.getLocalHost().getHostAddress();
        }
        catch (UnknownHostException e)
        {
        }
        return "127.0.0.1";
    }
    /**
     * 获取主机名
     *
     * @return 本地主机名
     */
    public static String getHostName()
    {
        try
        {
            return InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException e)
        {
        }
        return "未知";
    }
    /**
     * 从多级反向代理中获得第一个非unknown IP地址
     *
     * @param ip 获得的IP地址
     * @return 第一个非unknown IP地址
     */
    public static String getMultistageReverseProxyIp(String ip)
    {
        // 多级反向代理检测
        if (ip != null && ip.indexOf(",") > 0)
        {
            final String[] ips = ip.trim().split(",");
            for (String subIp : ips)
            {
                if (false == isUnknown(subIp))
                {
                    ip = subIp;
                    break;
                }
            }
        }
        return StringUtils.substring(ip, 0, 255);
    }
    /**
     * 检测给定字符串是否为未知,多用于检测HTTP请求相关
     *
     * @param checkString 被检测的字符串
     * @return 是否未知
     */
    public static boolean isUnknown(String checkString)
    {
        return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
    }
    /**
     * 是否为IP
     */
    public static boolean isIP(String ip)
    {
        return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
    }
    /**
     * 是否为IP,或 *为间隔的通配符地址
     */
    public static boolean isIpWildCard(String ip)
    {
        return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
    }
    /**
     * 检测参数是否在ip通配符里
     */
    public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip)
    {
        String[] s1 = ipWildCard.split("\\.");
        String[] s2 = ip.split("\\.");
        boolean isMatchedSeg = true;
        for (int i = 0; i < s1.length && !s1[i].equals("*"); i++)
        {
            if (!s1[i].equals(s2[i]))
            {
                isMatchedSeg = false;
                break;
            }
        }
        return isMatchedSeg;
    }
    /**
     * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
     */
    public static boolean isIPSegment(String ipSeg)
    {
        return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
    }
    /**
     * 判断ip是否在指定网段中
     */
    public static boolean ipIsInNetNoCheck(String iparea, String ip)
    {
        int idx = iparea.indexOf('-');
        String[] sips = iparea.substring(0, idx).split("\\.");
        String[] sipe = iparea.substring(idx + 1).split("\\.");
        String[] sipt = ip.split("\\.");
        long ips = 0L, ipe = 0L, ipt = 0L;
        for (int i = 0; i < 4; ++i)
        {
            ips = ips << 8 | Integer.parseInt(sips[i]);
            ipe = ipe << 8 | Integer.parseInt(sipe[i]);
            ipt = ipt << 8 | Integer.parseInt(sipt[i]);
        }
        if (ips > ipe)
        {
            long t = ips;
            ips = ipe;
            ipe = t;
        }
        return ips <= ipt && ipt <= ipe;
    }
    /**
     * 校验ip是否符合过滤串规则
     *
     * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
     * @param ip 校验IP地址
     * @return boolean 结果
     */
    public static boolean isMatchedIp(String filter, String ip)
    {
        if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip))
        {
            return false;
        }
        String[] ips = filter.split(";");
        for (String iStr : ips)
        {
            if (isIP(iStr) && iStr.equals(ip))
            {
                return true;
            }
            else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip))
            {
                return true;
            }
            else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip))
            {
                return true;
            }
        }
        return false;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.common.utils.poi;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Workbook;
/**
 * Excel数据格式处理适配器
 *
 * @author ruoyi
 */
public interface ExcelHandlerAdapter
{
    /**
     * 格式化
     *
     * @param value 单元格数据值
     * @param args excel注解args参数组
     * @param cell 单元格对象
     * @param wb 工作簿对象
     *
     * @return 处理后的值
     */
    Object format(Object value, String[] args, Cell cell, Workbook wb);
}
pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
New file
@@ -0,0 +1,1900 @@
package com.ruoyi.common.utils.poi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPicture;
import org.apache.poi.hssf.usermodel.HSSFPictureData;
import org.apache.poi.hssf.usermodel.HSSFShape;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
//import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFPicture;
import org.apache.poi.xssf.usermodel.XSSFShape;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type;
import com.ruoyi.common.annotation.Excels;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileTypeUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.ImageUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
/**
 * Excel相关处理
 *
 * @author ruoyi
 */
public class ExcelUtil<T>
{
    private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
    public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
    public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
    /**
     * 用于dictType属性数据存储,避免重复查缓存
     */
    public Map<String, String> sysDictMap = new HashMap<String, String>();
    /**
     * Excel sheet最大行数,默认65536
     */
    public static final int sheetSize = 65536;
    /**
     * 工作表名称
     */
    private String sheetName;
    /**
     * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
     */
    private Type type;
    /**
     * 工作薄对象
     */
    private Workbook wb;
    /**
     * 工作表对象
     */
    private Sheet sheet;
    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;
    /**
     * 导入导出数据列表
     */
    private List<T> list;
    /**
     * 注解列表
     */
    private List<Object[]> fields;
    /**
     * 当前行号
     */
    private int rownum;
    /**
     * 标题
     */
    private String title;
    /**
     * 最大高度
     */
    private short maxHeight;
    /**
     * 合并后最后行数
     */
    private int subMergedLastRowNum = 0;
    /**
     * 合并后开始行数
     */
    private int subMergedFirstRowNum = 1;
    /**
     * 对象的子列表方法
     */
    private Method subMethod;
    /**
     * 对象的子列表属性
     */
    private List<Field> subFields;
    /**
     * 统计列表
     */
    private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
    /**
     * 数字格式
     */
    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
    /**
     * 实体对象
     */
    public Class<T> clazz;
    /**
     * 需要显示列属性
     */
    public String[] includeFields;
    /**
     * 需要排除列属性
     */
    public String[] excludeFields;
    public ExcelUtil(Class<T> clazz)
    {
        this.clazz = clazz;
    }
    /**
     * 仅在Excel中显示列属性
     *
     * @param fields 列属性名 示例[单个"name"/多个"id","name"]
     */
    public void showColumn(String... fields)
    {
        this.includeFields = fields;
    }
    /**
     * 隐藏Excel中列属性
     *
     * @param fields 列属性名 示例[单个"name"/多个"id","name"]
     */
    public void hideColumn(String... fields)
    {
        this.excludeFields = fields;
    }
    public void init(List<T> list, String sheetName, String title, Type type)
    {
        if (list == null)
        {
            list = new ArrayList<T>();
        }
        this.list = list;
        this.sheetName = sheetName;
        this.type = type;
        this.title = title;
        createExcelField();
        createWorkbook();
        createTitle();
        createSubHead();
    }
    /**
     * 创建excel第一行标题
     */
    public void createTitle()
    {
        if (StringUtils.isNotEmpty(title))
        {
            int titleLastCol = this.fields.size() - 1;
            if (isSubList())
            {
                titleLastCol = titleLastCol + subFields.size() - 1;
            }
            Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
            titleRow.setHeightInPoints(30);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellStyle(styles.get("title"));
            titleCell.setCellValue(title);
            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol));
        }
    }
    /**
     * 创建对象的子列表名称
     */
    public void createSubHead()
    {
        if (isSubList())
        {
            Row subRow = sheet.createRow(rownum);
            int column = 0;
            int subFieldSize = subFields != null ? subFields.size() : 0;
            for (Object[] objects : fields)
            {
                Field field = (Field) objects[0];
                Excel attr = (Excel) objects[1];
                if (Collection.class.isAssignableFrom(field.getType()))
                {
                    Cell cell = subRow.createCell(column);
                    cell.setCellValue(attr.name());
                    cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
                    if (subFieldSize > 1)
                    {
                        CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);
                        sheet.addMergedRegion(cellAddress);
                    }
                    column += subFieldSize;
                }
                else
                {
                    Cell cell = subRow.createCell(column++);
                    cell.setCellValue(attr.name());
                    cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
                }
            }
            rownum++;
        }
    }
    /**
     * 对excel表单默认第一个索引名转换成list
     *
     * @param is 输入流
     * @return 转换后集合
     */
    public List<T> importExcel(InputStream is)
    {
        return importExcel(is, 0);
    }
    /**
     * 对excel表单默认第一个索引名转换成list
     *
     * @param is 输入流
     * @param titleNum 标题占用行数
     * @return 转换后集合
     */
    public List<T> importExcel(InputStream is, int titleNum)
    {
        List<T> list = null;
        try
        {
            list = importExcel(StringUtils.EMPTY, is, titleNum);
        }
        catch (Exception e)
        {
            log.error("导入Excel异常{}", e.getMessage());
            throw new UtilException(e.getMessage());
        }
        finally
        {
            IOUtils.closeQuietly(is);
        }
        return list;
    }
    /**
     * 对excel表单指定表格索引名转换成list
     *
     * @param sheetName 表格索引名
     * @param titleNum 标题占用行数
     * @param is 输入流
     * @return 转换后集合
     */
    public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception
    {
        this.type = Type.IMPORT;
        this.wb = WorkbookFactory.create(is);
        List<T> list = new ArrayList<T>();
        // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
        Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
        if (sheet == null)
        {
            throw new IOException("文件sheet不存在");
        }
        boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
        Map<String, PictureData> pictures;
        if (isXSSFWorkbook)
        {
            pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
        }
        else
        {
            pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
        }
        // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
        int rows = sheet.getLastRowNum();
        if (rows > 0)
        {
            // 定义一个map用于存放excel列的序号和field.
            Map<String, Integer> cellMap = new HashMap<String, Integer>();
            // 获取表头
            Row heard = sheet.getRow(titleNum);
            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
            {
                Cell cell = heard.getCell(i);
                if (StringUtils.isNotNull(cell))
                {
                    String value = this.getCellValue(heard, i).toString();
                    cellMap.put(value, i);
                }
                else
                {
                    cellMap.put(null, i);
                }
            }
            // 有数据时才处理 得到类的所有field.
            List<Object[]> fields = this.getFields();
            Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
            for (Object[] objects : fields)
            {
                Excel attr = (Excel) objects[1];
                Integer column = cellMap.get(attr.name());
                if (column != null)
                {
                    fieldsMap.put(column, objects);
                }
            }
            for (int i = titleNum + 1; i <= rows; i++)
            {
                // 从第2行开始取数据,默认第一行是表头.
                Row row = sheet.getRow(i);
                // 判断当前行是否是空行
                if (isRowEmpty(row))
                {
                    continue;
                }
                T entity = null;
                for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet())
                {
                    Object val = this.getCellValue(row, entry.getKey());
                    // 如果不存在实例则新建.
                    entity = (entity == null ? clazz.newInstance() : entity);
                    // 从map中得到对应列的field.
                    Field field = (Field) entry.getValue()[0];
                    Excel attr = (Excel) entry.getValue()[1];
                    // 取得类型,并根据对象类型设置值.
                    Class<?> fieldType = field.getType();
                    if (String.class == fieldType)
                    {
                        String s = Convert.toStr(val);
                        if (StringUtils.endsWith(s, ".0"))
                        {
                            val = StringUtils.substringBefore(s, ".0");
                        }
                        else
                        {
                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
                            if (StringUtils.isNotEmpty(dateFormat))
                            {
                                val = parseDateToStr(dateFormat, val);
                            }
                            else
                            {
                                val = Convert.toStr(val);
                            }
                        }
                    }
                    else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
                    {
                        val = Convert.toInt(val);
                    }
                    else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
                    {
                        val = Convert.toLong(val);
                    }
                    else if (Double.TYPE == fieldType || Double.class == fieldType)
                    {
                        val = Convert.toDouble(val);
                    }
                    else if (Float.TYPE == fieldType || Float.class == fieldType)
                    {
                        val = Convert.toFloat(val);
                    }
                    else if (BigDecimal.class == fieldType)
                    {
                        val = Convert.toBigDecimal(val);
                    }
                    else if (Date.class == fieldType)
                    {
                        if (val instanceof String)
                        {
                            val = DateUtils.parseDate(val);
                        }
                        else if (val instanceof Double)
                        {
                            val = DateUtil.getJavaDate((Double) val);
                        }
                    }
                    else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
                    {
                        val = Convert.toBool(val, false);
                    }
                    if (StringUtils.isNotNull(fieldType))
                    {
                        String propertyName = field.getName();
                        if (StringUtils.isNotEmpty(attr.targetAttr()))
                        {
                            propertyName = field.getName() + "." + attr.targetAttr();
                        }
                        if (StringUtils.isNotEmpty(attr.readConverterExp()))
                        {
                            val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
                        }
                        else if (StringUtils.isNotEmpty(attr.dictType()))
                        {
                            if (!sysDictMap.containsKey(attr.dictType() + val))
                            {
                                String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
                                sysDictMap.put(attr.dictType() + val, dictValue);
                            }
                            val = sysDictMap.get(attr.dictType() + val);
                        }
                        else if (!attr.handler().equals(ExcelHandlerAdapter.class))
                        {
                            val = dataFormatHandlerAdapter(val, attr, null);
                        }
                        else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
                        {
                            PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
                            if (image == null)
                            {
                                val = "";
                            }
                            else
                            {
                                byte[] data = image.getData();
                                val = FileUtils.writeImportBytes(data);
                            }
                        }
                        ReflectUtils.invokeSetter(entity, propertyName, val);
                    }
                }
                list.add(entity);
            }
        }
        return list;
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     * @return 结果
     */
    public AjaxResult exportExcel(List<T> list, String sheetName)
    {
        return exportExcel(list, sheetName, StringUtils.EMPTY);
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     * @param title 标题
     * @return 结果
     */
    public AjaxResult exportExcel(List<T> list, String sheetName, String title)
    {
        this.init(list, sheetName, title, Type.EXPORT);
        return exportExcel();
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param response 返回数据
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     * @return 结果
     */
    public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)
    {
        exportExcel(response, list, sheetName, StringUtils.EMPTY);
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param response 返回数据
     * @param list 导出数据集合
     * @param sheetName 工作表的名称
     * @param title 标题
     * @return 结果
     */
    public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title)
    {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        this.init(list, sheetName, title, Type.EXPORT);
        exportExcel(response);
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param sheetName 工作表的名称
     * @return 结果
     */
    public AjaxResult importTemplateExcel(String sheetName)
    {
        return importTemplateExcel(sheetName, StringUtils.EMPTY);
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param sheetName 工作表的名称
     * @param title 标题
     * @return 结果
     */
    public AjaxResult importTemplateExcel(String sheetName, String title)
    {
        this.init(null, sheetName, title, Type.IMPORT);
        return exportExcel();
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param sheetName 工作表的名称
     * @return 结果
     */
    public void importTemplateExcel(HttpServletResponse response, String sheetName)
    {
        importTemplateExcel(response, sheetName, StringUtils.EMPTY);
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param sheetName 工作表的名称
     * @param title 标题
     * @return 结果
     */
    public void importTemplateExcel(HttpServletResponse response, String sheetName, String title)
    {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        this.init(null, sheetName, title, Type.IMPORT);
        exportExcel(response);
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @return 结果
     */
    public void exportExcel(HttpServletResponse response)
    {
        try
        {
            writeSheet();
            wb.write(response.getOutputStream());
        }
        catch (Exception e)
        {
            log.error("导出Excel异常{}", e.getMessage());
        }
        finally
        {
            IOUtils.closeQuietly(wb);
        }
    }
    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @return 结果
     */
    public AjaxResult exportExcel()
    {
        OutputStream out = null;
        try
        {
            writeSheet();
            String filename = encodingFilename(sheetName);
            out = new FileOutputStream(getAbsoluteFile(filename));
            wb.write(out);
            return AjaxResult.success(filename);
        }
        catch (Exception e)
        {
            log.error("导出Excel异常{}", e.getMessage());
            throw new UtilException("导出Excel失败,请联系网站管理员!");
        }
        finally
        {
            IOUtils.closeQuietly(wb);
            IOUtils.closeQuietly(out);
        }
    }
    /**
     * 创建写入数据到Sheet
     */
    public void writeSheet()
    {
        // 取出一共有多少个sheet.
        int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
        for (int index = 0; index < sheetNo; index++)
        {
            createSheet(sheetNo, index);
            // 产生一行
            Row row = sheet.createRow(rownum);
            int column = 0;
            // 写入各个字段的列头名称
            for (Object[] os : fields)
            {
                Field field = (Field) os[0];
                Excel excel = (Excel) os[1];
                if (Collection.class.isAssignableFrom(field.getType()))
                {
                    for (Field subField : subFields)
                    {
                        Excel subExcel = subField.getAnnotation(Excel.class);
                        this.createHeadCell(subExcel, row, column++);
                    }
                }
                else
                {
                    this.createHeadCell(excel, row, column++);
                }
            }
            if (Type.EXPORT.equals(type))
            {
                fillExcelData(index, row);
                addStatisticsRow();
            }
        }
    }
    /**
     * 填充excel数据
     *
     * @param index 序号
     * @param row 单元格行
     */
    @SuppressWarnings("unchecked")
    public void fillExcelData(int index, Row row)
    {
        int startNo = index * sheetSize;
        int endNo = Math.min(startNo + sheetSize, list.size());
        int currentRowNum = rownum + 1; // 从标题行后开始
        for (int i = startNo; i < endNo; i++)
        {
            row = sheet.createRow(currentRowNum);
            T vo = (T) list.get(i);
            int column = 0;
            int maxSubListSize = getCurrentMaxSubListSize(vo);
            for (Object[] os : fields)
            {
                Field field = (Field) os[0];
                Excel excel = (Excel) os[1];
                if (Collection.class.isAssignableFrom(field.getType()))
                {
                    try
                    {
                        Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
                        if (subList != null && !subList.isEmpty())
                        {
                            int subIndex = 0;
                            for (Object subVo : subList)
                            {
                                Row subRow = sheet.getRow(currentRowNum + subIndex);
                                if (subRow == null)
                                {
                                    subRow = sheet.createRow(currentRowNum + subIndex);
                                }
                                int subColumn = column;
                                for (Field subField : subFields)
                                {
                                    Excel subExcel = subField.getAnnotation(Excel.class);
                                    addCell(subExcel, subRow, (T) subVo, subField, subColumn++);
                                }
                                subIndex++;
                            }
                            column += subFields.size();
                        }
                    }
                    catch (Exception e)
                    {
                        log.error("填充集合数据失败", e);
                    }
                }
                else
                {
                    // 创建单元格并设置值
                    addCell(excel, row, vo, field, column);
                    if (maxSubListSize > 1 && excel.needMerge())
                    {
                        sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column));
                    }
                    column++;
                }
            }
            currentRowNum += maxSubListSize;
        }
    }
    /**
     * 获取子列表最大数
     */
    private int getCurrentMaxSubListSize(T vo)
    {
        int maxSubListSize = 1;
        for (Object[] os : fields)
        {
            Field field = (Field) os[0];
            if (Collection.class.isAssignableFrom(field.getType()))
            {
                try
                {
                    Collection<?> subList = (Collection<?>) getTargetValue(vo, field, (Excel) os[1]);
                    if (subList != null && !subList.isEmpty())
                    {
                        maxSubListSize = Math.max(maxSubListSize, subList.size());
                    }
                }
                catch (Exception e)
                {
                    log.error("获取集合大小失败", e);
                }
            }
        }
        return maxSubListSize;
    }
    /**
     * 创建表格样式
     *
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(Workbook wb)
    {
        // 写入各条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setFontHeightInPoints((short) 16);
        titleFont.setBold(true);
        style.setFont(titleFont);
        DataFormat dataFormat = wb.createDataFormat();
        style.setDataFormat(dataFormat.getFormat("@"));
        styles.put("title", style);
        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);
        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font totalFont = wb.createFont();
        totalFont.setFontName("Arial");
        totalFont.setFontHeightInPoints((short) 10);
        style.setFont(totalFont);
        styles.put("total", style);
        styles.putAll(annotationHeaderStyles(wb, styles));
        styles.putAll(annotationDataStyles(wb));
        return styles;
    }
    /**
     * 根据Excel注解创建表格头样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles)
    {
        Map<String, CellStyle> headerStyles = new HashMap<String, CellStyle>();
        for (Object[] os : fields)
        {
            Excel excel = (Excel) os[1];
            String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor());
            if (!headerStyles.containsKey(key))
            {
                CellStyle style = wb.createCellStyle();
                style.cloneStyleFrom(styles.get("data"));
                style.setAlignment(HorizontalAlignment.CENTER);
                style.setVerticalAlignment(VerticalAlignment.CENTER);
                style.setFillForegroundColor(excel.headerBackgroundColor().index);
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                Font headerFont = wb.createFont();
                headerFont.setFontName("Arial");
                headerFont.setFontHeightInPoints((short) 10);
                headerFont.setBold(true);
                headerFont.setColor(excel.headerColor().index);
                style.setFont(headerFont);
                // 设置表格头单元格文本形式
                DataFormat dataFormat = wb.createDataFormat();
                style.setDataFormat(dataFormat.getFormat("@"));
                headerStyles.put(key, style);
            }
        }
        return headerStyles;
    }
    /**
     * 根据Excel注解创建表格列样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> annotationDataStyles(Workbook wb)
    {
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        for (Object[] os : fields)
        {
            Field field = (Field) os[0];
            Excel excel = (Excel) os[1];
            if (Collection.class.isAssignableFrom(field.getType()))
            {
                ParameterizedType pt = (ParameterizedType) field.getGenericType();
                Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
                List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
                for (Field subField : subFields)
                {
                    Excel subExcel = subField.getAnnotation(Excel.class);
                    annotationDataStyles(styles, subField, subExcel);
                }
            }
            else
            {
                annotationDataStyles(styles, field, excel);
            }
        }
        return styles;
    }
    /**
     * 根据Excel注解创建表格列样式
     *
     * @param styles 自定义样式列表
     * @param field  属性列信息
     * @param excel  注解信息
     */
    public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel)
    {
        String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText());
        if (!styles.containsKey(key))
        {
            CellStyle style = wb.createCellStyle();
            style.setAlignment(excel.align());
            style.setVerticalAlignment(VerticalAlignment.CENTER);
            style.setBorderRight(BorderStyle.THIN);
            style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setBorderLeft(BorderStyle.THIN);
            style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setBorderTop(BorderStyle.THIN);
            style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setBorderBottom(BorderStyle.THIN);
            style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            style.setFillForegroundColor(excel.backgroundColor().getIndex());
            style.setWrapText(excel.wrapText());
            Font dataFont = wb.createFont();
            dataFont.setFontName("Arial");
            dataFont.setFontHeightInPoints((short) 10);
            dataFont.setColor(excel.color().index);
            style.setFont(dataFont);
            if (ColumnType.TEXT == excel.cellType())
            {
                DataFormat dataFormat = wb.createDataFormat();
                style.setDataFormat(dataFormat.getFormat("@"));
            }
            styles.put(key, style);
        }
    }
    /**
     * 创建单元格
     */
    public Cell createHeadCell(Excel attr, Row row, int column)
    {
        // 创建列
        Cell cell = row.createCell(column);
        // 写入列信息
        cell.setCellValue(attr.name());
        setDataValidation(attr, row, column);
        cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
        if (isSubList())
        {
            // 填充默认样式,防止合并单元格样式失效
            sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
            if (attr.needMerge())
            {
                sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
            }
        }
        return cell;
    }
    /**
     * 设置单元格信息
     *
     * @param value 单元格值
     * @param attr 注解相关
     * @param cell 单元格信息
     */
    public void setCellVo(Object value, Excel attr, Cell cell)
    {
        if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType())
        {
            String cellValue = Convert.toStr(value);
            // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。
            if (StringUtils.startsWithAny(cellValue, FORMULA_STR))
            {
                cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");
            }
            if (value instanceof Collection && StringUtils.equals("[]", cellValue))
            {
                cellValue = StringUtils.EMPTY;
            }
            cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());
        }
        else if (ColumnType.NUMERIC == attr.cellType())
        {
            if (StringUtils.isNotNull(value))
            {
                cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
            }
        }
        else if (ColumnType.IMAGE == attr.cellType())
        {
            ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
            String imagePath = Convert.toStr(value);
            if (StringUtils.isNotEmpty(imagePath))
            {
                byte[] data = ImageUtils.getImage(imagePath);
                getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
                        cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
            }
        }
    }
    /**
     * 获取画布
     */
    public static Drawing<?> getDrawingPatriarch(Sheet sheet)
    {
        if (sheet.getDrawingPatriarch() == null)
        {
            sheet.createDrawingPatriarch();
        }
        return sheet.getDrawingPatriarch();
    }
    /**
     * 获取图片类型,设置图片插入类型
     */
    public int getImageType(byte[] value)
    {
        String type = FileTypeUtils.getFileExtendName(value);
        if ("JPG".equalsIgnoreCase(type))
        {
            return Workbook.PICTURE_TYPE_JPEG;
        }
        else if ("PNG".equalsIgnoreCase(type))
        {
            return Workbook.PICTURE_TYPE_PNG;
        }
        return Workbook.PICTURE_TYPE_JPEG;
    }
    /**
     * 创建表格样式
     */
    public void setDataValidation(Excel attr, Row row, int column)
    {
        if (attr.name().indexOf("注:") >= 0)
        {
            sheet.setColumnWidth(column, 6000);
        }
        else
        {
            // 设置列宽
            sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
        }
        if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict())
        {
            String[] comboArray = attr.combo();
            if (attr.comboReadDict())
            {
                if (!sysDictMap.containsKey("combo_" + attr.dictType()))
                {
                    String labels = DictUtils.getDictLabels(attr.dictType());
                    sysDictMap.put("combo_" + attr.dictType(), labels);
                }
                String val = sysDictMap.get("combo_" + attr.dictType());
                comboArray = StringUtils.split(val, DictUtils.SEPARATOR);
            }
            if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255)
            {
                // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到
                setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column);
            }
            else
            {
                // 提示信息或只能选择不能输入的列内容.
                setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column);
            }
        }
    }
    /**
     * 添加单元格
     */
    public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
    {
        Cell cell = null;
        try
        {
            // 设置行高
            row.setHeight(maxHeight);
            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
            if (attr.isExport())
            {
                // 创建cell
                cell = row.createCell(column);
                if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
                {
                    if (subMergedLastRowNum >= subMergedFirstRowNum)
                    {
                        sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column));
                    }
                }
                cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
                // 用于读取对象中的属性
                Object value = getTargetValue(vo, field, attr);
                String dateFormat = attr.dateFormat();
                String readConverterExp = attr.readConverterExp();
                String separator = attr.separator();
                String dictType = attr.dictType();
                if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
                {
                    cell.getCellStyle().setDataFormat(this.wb.getCreationHelper().createDataFormat().getFormat(dateFormat));
                    cell.setCellValue(parseDateToStr(dateFormat, value));
                }
                else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
                {
                    cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
                }
                else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value))
                {
                    if (!sysDictMap.containsKey(dictType + value))
                    {
                        String lable = convertDictByExp(Convert.toStr(value), dictType, separator);
                        sysDictMap.put(dictType + value, lable);
                    }
                    cell.setCellValue(sysDictMap.get(dictType + value));
                }
                else if (value instanceof BigDecimal && -1 != attr.scale())
                {
                    cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue());
                }
                else if (!attr.handler().equals(ExcelHandlerAdapter.class))
                {
                    cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell));
                }
                else
                {
                    // 设置列类型
                    setCellVo(value, attr, cell);
                }
                addStatisticsData(column, Convert.toStr(value), attr);
            }
        }
        catch (Exception e)
        {
            log.error("导出Excel失败{}", e);
        }
        return cell;
    }
    /**
     * 设置 POI XSSFSheet 单元格提示或选择框
     *
     * @param sheet 表单
     * @param textlist 下拉框显示的内容
     * @param promptContent 提示内容
     * @param firstRow 开始行
     * @param endRow 结束行
     * @param firstCol 开始列
     * @param endCol 结束列
     */
    public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow,
            int firstCol, int endCol)
    {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1");
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        if (StringUtils.isNotEmpty(promptContent))
        {
            // 如果设置了提示信息则鼠标放上去提示
            dataValidation.createPromptBox("", promptContent);
            dataValidation.setShowPromptBox(true);
        }
        // 处理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation)
        {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        }
        else
        {
            dataValidation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(dataValidation);
    }
    /**
     * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框).
     *
     * @param sheet 要设置的sheet.
     * @param textlist 下拉框显示的内容
     * @param promptContent 提示内容
     * @param firstRow 开始行
     * @param endRow 结束行
     * @param firstCol 开始列
     * @param endCol 结束列
     */
    public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol)
    {
        String hideSheetName = "combo_" + firstCol + "_" + endCol;
        Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
        for (int i = 0; i < textlist.length; i++)
        {
            hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]);
        }
        // 创建名称,可被其他单元格引用
        Name name = wb.createName();
        name.setNameName(hideSheetName + "_data");
        name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length);
        DataValidationHelper helper = sheet.getDataValidationHelper();
        // 加载下拉列表内容
        DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");
        // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        // 数据有效性对象
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        if (StringUtils.isNotEmpty(promptContent))
        {
            // 如果设置了提示信息则鼠标放上去提示
            dataValidation.createPromptBox("", promptContent);
            dataValidation.setShowPromptBox(true);
        }
        // 处理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation)
        {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        }
        else
        {
            dataValidation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(dataValidation);
        // 设置hiddenSheet隐藏
        wb.setSheetHidden(wb.getSheetIndex(hideSheet), true);
    }
    /**
     * 解析导出值 0=男,1=女,2=未知
     *
     * @param propertyValue 参数值
     * @param converterExp 翻译注解
     * @param separator 分隔符
     * @return 解析后值
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource)
        {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator))
            {
                for (String value : propertyValue.split(separator))
                {
                    if (itemArray[0].equals(value))
                    {
                        propertyString.append(itemArray[1] + separator);
                        break;
                    }
                }
            }
            else
            {
                if (itemArray[0].equals(propertyValue))
                {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * 反向解析值 男=0,女=1,未知=2
     *
     * @param propertyValue 参数值
     * @param converterExp 翻译注解
     * @param separator 分隔符
     * @return 解析后值
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource)
        {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator))
            {
                for (String value : propertyValue.split(separator))
                {
                    if (itemArray[1].equals(value))
                    {
                        propertyString.append(itemArray[0] + separator);
                        break;
                    }
                }
            }
            else
            {
                if (itemArray[1].equals(propertyValue))
                {
                    return itemArray[0];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * 解析字典值
     *
     * @param dictValue 字典值
     * @param dictType 字典类型
     * @param separator 分隔符
     * @return 字典标签
     */
    public static String convertDictByExp(String dictValue, String dictType, String separator)
    {
        return DictUtils.getDictLabel(dictType, dictValue, separator);
    }
    /**
     * 反向解析值字典值
     *
     * @param dictLabel 字典标签
     * @param dictType 字典类型
     * @param separator 分隔符
     * @return 字典值
     */
    public static String reverseDictByExp(String dictLabel, String dictType, String separator)
    {
        return DictUtils.getDictValue(dictType, dictLabel, separator);
    }
    /**
     * 数据处理器
     *
     * @param value 数据值
     * @param excel 数据注解
     * @return
     */
    public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell)
    {
        try
        {
            Object instance = excel.handler().newInstance();
            Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
            value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
        }
        catch (Exception e)
        {
            log.error("不能格式化数据 " + excel.handler(), e.getMessage());
        }
        return Convert.toStr(value);
    }
    /**
     * 合计统计信息
     */
    private void addStatisticsData(Integer index, String text, Excel entity)
    {
        if (entity != null && entity.isStatistics())
        {
            Double temp = 0D;
            if (!statistics.containsKey(index))
            {
                statistics.put(index, temp);
            }
            try
            {
                temp = Double.valueOf(text);
            }
            catch (NumberFormatException e)
            {
            }
            statistics.put(index, statistics.get(index) + temp);
        }
    }
    /**
     * 创建统计行
     */
    public void addStatisticsRow()
    {
        if (statistics.size() > 0)
        {
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
            Set<Integer> keys = statistics.keySet();
            Cell cell = row.createCell(0);
            cell.setCellStyle(styles.get("total"));
            cell.setCellValue("合计");
            for (Integer key : keys)
            {
                cell = row.createCell(key);
                cell.setCellStyle(styles.get("total"));
                cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
            }
            statistics.clear();
        }
    }
    /**
     * 编码文件名
     */
    public String encodingFilename(String filename)
    {
        return UUID.randomUUID() + "_" + filename + ".xlsx";
    }
    /**
     * 获取下载路径
     *
     * @param filename 文件名称
     */
    public String getAbsoluteFile(String filename)
    {
        String downloadPath = RuoYiConfig.getDownloadPath() + filename;
        File desc = new File(downloadPath);
        if (!desc.getParentFile().exists())
        {
            desc.getParentFile().mkdirs();
        }
        return downloadPath;
    }
    /**
     * 获取bean中的属性值
     *
     * @param vo 实体对象
     * @param field 字段
     * @param excel 注解
     * @return 最终的属性值
     * @throws Exception
     */
    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
    {
        field.setAccessible(true);
        Object o = field.get(vo);
        if (StringUtils.isNotEmpty(excel.targetAttr()))
        {
            String target = excel.targetAttr();
            if (target.contains("."))
            {
                String[] targets = target.split("[.]");
                for (String name : targets)
                {
                    o = getValue(o, name);
                }
            }
            else
            {
                o = getValue(o, target);
            }
        }
        return o;
    }
    /**
     * 以类的属性的get方法方法形式获取值
     *
     * @param o
     * @param name
     * @return value
     * @throws Exception
     */
    private Object getValue(Object o, String name) throws Exception
    {
        if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name))
        {
            Class<?> clazz = o.getClass();
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            o = field.get(o);
        }
        return o;
    }
    /**
     * 得到所有定义字段
     */
    private void createExcelField()
    {
        this.fields = getFields();
        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
        this.maxHeight = getRowHeight();
    }
    /**
     * 获取字段注解信息
     */
    public List<Object[]> getFields()
    {
        List<Object[]> fields = new ArrayList<Object[]>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        if (StringUtils.isNotEmpty(includeFields))
        {
            for (Field field : tempFields)
            {
                if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class))
                {
                    addField(fields, field);
                }
            }
        }
        else if (StringUtils.isNotEmpty(excludeFields))
        {
            for (Field field : tempFields)
            {
                if (!ArrayUtils.contains(this.excludeFields, field.getName()))
                {
                    addField(fields, field);
                }
            }
        }
        else
        {
            for (Field field : tempFields)
            {
                addField(fields, field);
            }
        }
        return fields;
    }
    /**
     * 添加字段信息
     */
    public void addField(List<Object[]> fields, Field field)
    {
        // 单注解
        if (field.isAnnotationPresent(Excel.class))
        {
            Excel attr = field.getAnnotation(Excel.class);
            if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
            {
                fields.add(new Object[] { field, attr });
            }
            if (Collection.class.isAssignableFrom(field.getType()))
            {
                subMethod = getSubMethod(field.getName(), clazz);
                ParameterizedType pt = (ParameterizedType) field.getGenericType();
                Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
                this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
            }
        }
        // 多注解
        if (field.isAnnotationPresent(Excels.class))
        {
            Excels attrs = field.getAnnotation(Excels.class);
            Excel[] excels = attrs.value();
            for (Excel attr : excels)
            {
                if (StringUtils.isNotEmpty(includeFields))
                {
                    if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr())
                            && (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
                    {
                        fields.add(new Object[] { field, attr });
                    }
                }
                else
                {
                    if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
                            && (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
                    {
                        fields.add(new Object[] { field, attr });
                    }
                }
            }
        }
    }
    /**
     * 根据注解获取最大行高
     */
    public short getRowHeight()
    {
        double maxHeight = 0;
        for (Object[] os : this.fields)
        {
            Excel excel = (Excel) os[1];
            maxHeight = Math.max(maxHeight, excel.height());
        }
        return (short) (maxHeight * 20);
    }
    /**
     * 创建一个工作簿
     */
    public void createWorkbook()
    {
        this.wb = new SXSSFWorkbook(500);
        this.sheet = wb.createSheet();
        wb.setSheetName(0, sheetName);
        this.styles = createStyles(wb);
    }
    /**
     * 创建工作表
     *
     * @param sheetNo sheet数量
     * @param index 序号
     */
    public void createSheet(int sheetNo, int index)
    {
        // 设置工作表的名称.
        if (sheetNo > 1 && index > 0)
        {
            this.sheet = wb.createSheet();
            this.createTitle();
            wb.setSheetName(index, sheetName + index);
        }
    }
    /**
     * 获取单元格值
     *
     * @param row 获取的行
     * @param column 获取单元格列号
     * @return 单元格值
     */
    public Object getCellValue(Row row, int column)
    {
        if (row == null)
        {
            return row;
        }
        Object val = "";
        try
        {
            Cell cell = row.getCell(column);
//            if (StringUtils.isNotNull(cell))
//            {
//                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA)
//                {
//                    val = cell.getNumericCellValue();
//                    if (DateUtil.isCellDateFormatted(cell))
//                    {
//                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
//                    }
//                    else
//                    {
//                        if ((Double) val % 1 != 0)
//                        {
//                            val = new BigDecimal(val.toString());
//                        }
//                        else
//                        {
//                            val = new DecimalFormat("0").format(val);
//                        }
//                    }
//                }
//                else if (cell.getCellType() == CellType.STRING)
//                {
//                    val = cell.getStringCellValue();
//                }
//                else if (cell.getCellType() == CellType.BOOLEAN)
//                {
//                    val = cell.getBooleanCellValue();
//                }
//                else if (cell.getCellType() == CellType.ERROR)
//                {
//                    val = cell.getErrorCellValue();
//                }
//
//            }
        }
        catch (Exception e)
        {
            return val;
        }
        return val;
    }
    /**
     * 判断是否是空行
     *
     * @param row 判断的行
     * @return
     */
    private boolean isRowEmpty(Row row)
    {
        if (row == null)
        {
            return true;
        }
        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++)
        {
            Cell cell = row.getCell(i);
//            if (cell != null && cell.getCellType() != CellType.BLANK)
//            {
//                return false;
//            }
        }
        return true;
    }
    /**
     * 获取Excel2003图片
     *
     * @param sheet 当前sheet对象
     * @param workbook 工作簿对象
     * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
     */
    public static Map<String, PictureData> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook)
    {
        Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
        List<HSSFPictureData> pictures = workbook.getAllPictures();
        if (!pictures.isEmpty())
        {
            for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren())
            {
                HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
                if (shape instanceof HSSFPicture)
                {
                    HSSFPicture pic = (HSSFPicture) shape;
                    int pictureIndex = pic.getPictureIndex() - 1;
                    HSSFPictureData picData = pictures.get(pictureIndex);
                    String picIndex = anchor.getRow1() + "_" + anchor.getCol1();
                    sheetIndexPicMap.put(picIndex, picData);
                }
            }
            return sheetIndexPicMap;
        }
        else
        {
            return sheetIndexPicMap;
        }
    }
    /**
     * 获取Excel2007图片
     *
     * @param sheet 当前sheet对象
     * @param workbook 工作簿对象
     * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
     */
    public static Map<String, PictureData> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook)
    {
        Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
//        for (POIXMLDocumentPart dr : sheet.getRelations())
//        {
//            if (dr instanceof XSSFDrawing)
//            {
//                XSSFDrawing drawing = (XSSFDrawing) dr;
//                List<XSSFShape> shapes = drawing.getShapes();
//                for (XSSFShape shape : shapes)
//                {
//                    if (shape instanceof XSSFPicture)
//                    {
//                        XSSFPicture pic = (XSSFPicture) shape;
//                        XSSFClientAnchor anchor = pic.getPreferredSize();
//                        CTMarker ctMarker = anchor.getFrom();
//                        String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
//                        sheetIndexPicMap.put(picIndex, pic.getPictureData());
//                    }
//                }
//            }
//        }
        return sheetIndexPicMap;
    }
    /**
     * 格式化不同类型的日期对象
     *
     * @param dateFormat 日期格式
     * @param val 被格式化的日期对象
     * @return 格式化后的日期字符
     */
    public String parseDateToStr(String dateFormat, Object val)
    {
        if (val == null)
        {
            return "";
        }
        String str;
        if (val instanceof Date)
        {
            str = DateUtils.parseDateToStr(dateFormat, (Date) val);
        }
        else if (val instanceof LocalDateTime)
        {
            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));
        }
        else if (val instanceof LocalDate)
        {
            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));
        }
        else
        {
            str = val.toString();
        }
        return str;
    }
    /**
     * 是否有对象的子列表
     */
    public boolean isSubList()
    {
        return StringUtils.isNotNull(subFields) && subFields.size() > 0;
    }
    /**
     * 是否有对象的子列表,集合不为空
     */
    public boolean isSubListValue(T vo)
    {
        return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;
    }
    /**
     * 获取集合的值
     */
    public Collection<?> getListCellValue(Object obj)
    {
        Object value;
        try
        {
            value = subMethod.invoke(obj, new Object[] {});
        }
        catch (Exception e)
        {
            return new ArrayList<Object>();
        }
        return (Collection<?>) value;
    }
    /**
     * 获取对象的子列表方法
     *
     * @param name 名称
     * @param pojoClass 类对象
     * @return 子列表方法
     */
    public Method getSubMethod(String name, Class<?> pojoClass)
    {
        StringBuffer getMethodName = new StringBuffer("get");
        getMethodName.append(name.substring(0, 1).toUpperCase());
        getMethodName.append(name.substring(1));
        Method method = null;
        try
        {
            method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});
        }
        catch (Exception e)
        {
            log.error("获取对象异常{}", e.getMessage());
        }
        return method;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
New file
@@ -0,0 +1,410 @@
package com.ruoyi.common.utils.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
/**
 * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
 *
 * @author ruoyi
 */
@SuppressWarnings("rawtypes")
public class ReflectUtils
{
    private static final String SETTER_PREFIX = "set";
    private static final String GETTER_PREFIX = "get";
    private static final String CGLIB_CLASS_SEPARATOR = "$$";
    private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
    /**
     * 调用Getter方法.
     * 支持多级,如:对象名.对象名.方法
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeGetter(Object obj, String propertyName)
    {
        Object object = obj;
        for (String name : StringUtils.split(propertyName, "."))
        {
            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
            object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
        }
        return (E) object;
    }
    /**
     * 调用Setter方法, 仅匹配方法名。
     * 支持多级,如:对象名.对象名.方法
     */
    public static <E> void invokeSetter(Object obj, String propertyName, E value)
    {
        Object object = obj;
        String[] names = StringUtils.split(propertyName, ".");
        for (int i = 0; i < names.length; i++)
        {
            if (i < names.length - 1)
            {
                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
                object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
            }
            else
            {
                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
                invokeMethodByName(object, setterMethodName, new Object[] { value });
            }
        }
    }
    /**
     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
     */
    @SuppressWarnings("unchecked")
    public static <E> E getFieldValue(final Object obj, final String fieldName)
    {
        Field field = getAccessibleField(obj, fieldName);
        if (field == null)
        {
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
            return null;
        }
        E result = null;
        try
        {
            result = (E) field.get(obj);
        }
        catch (IllegalAccessException e)
        {
            logger.error("不可能抛出的异常{}", e.getMessage());
        }
        return result;
    }
    /**
     * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
     */
    public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
    {
        Field field = getAccessibleField(obj, fieldName);
        if (field == null)
        {
            // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
            return;
        }
        try
        {
            field.set(obj, value);
        }
        catch (IllegalAccessException e)
        {
            logger.error("不可能抛出的异常: {}", e.getMessage());
        }
    }
    /**
     * 直接调用对象方法, 无视private/protected修饰符.
     * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
     * 同时匹配方法名+参数类型,
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
            final Object[] args)
    {
        if (obj == null || methodName == null)
        {
            return null;
        }
        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
        if (method == null)
        {
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
            return null;
        }
        try
        {
            return (E) method.invoke(obj, args);
        }
        catch (Exception e)
        {
            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
            throw convertReflectionExceptionToUnchecked(msg, e);
        }
    }
    /**
     * 直接调用对象方法, 无视private/protected修饰符,
     * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
     * 只匹配函数名,如果有多个同名函数调用第一个。
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
    {
        Method method = getAccessibleMethodByName(obj, methodName, args.length);
        if (method == null)
        {
            // 如果为空不报错,直接返回空。
            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
            return null;
        }
        try
        {
            // 类型转换(将参数数据类型转换为目标方法参数类型)
            Class<?>[] cs = method.getParameterTypes();
            for (int i = 0; i < cs.length; i++)
            {
                if (args[i] != null && !args[i].getClass().equals(cs[i]))
                {
                    if (cs[i] == String.class)
                    {
                        args[i] = Convert.toStr(args[i]);
                        if (StringUtils.endsWith((String) args[i], ".0"))
                        {
                            args[i] = StringUtils.substringBefore((String) args[i], ".0");
                        }
                    }
                    else if (cs[i] == Integer.class)
                    {
                        args[i] = Convert.toInt(args[i]);
                    }
                    else if (cs[i] == Long.class)
                    {
                        args[i] = Convert.toLong(args[i]);
                    }
                    else if (cs[i] == Double.class)
                    {
                        args[i] = Convert.toDouble(args[i]);
                    }
                    else if (cs[i] == Float.class)
                    {
                        args[i] = Convert.toFloat(args[i]);
                    }
                    else if (cs[i] == Date.class)
                    {
                        if (args[i] instanceof String)
                        {
                            args[i] = DateUtils.parseDate(args[i]);
                        }
                        else
                        {
                            args[i] = DateUtil.getJavaDate((Double) args[i]);
                        }
                    }
                    else if (cs[i] == boolean.class || cs[i] == Boolean.class)
                    {
                        args[i] = Convert.toBool(args[i]);
                    }
                }
            }
            return (E) method.invoke(obj, args);
        }
        catch (Exception e)
        {
            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
            throw convertReflectionExceptionToUnchecked(msg, e);
        }
    }
    /**
     * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
     * 如向上转型到Object仍无法找到, 返回null.
     */
    public static Field getAccessibleField(final Object obj, final String fieldName)
    {
        // 为空不报错。直接返回 null
        if (obj == null)
        {
            return null;
        }
        Validate.notBlank(fieldName, "fieldName can't be blank");
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
        {
            try
            {
                Field field = superClass.getDeclaredField(fieldName);
                makeAccessible(field);
                return field;
            }
            catch (NoSuchFieldException e)
            {
                continue;
            }
        }
        return null;
    }
    /**
     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
     * 如向上转型到Object仍无法找到, 返回null.
     * 匹配函数名+参数类型。
     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethod(final Object obj, final String methodName,
            final Class<?>... parameterTypes)
    {
        // 为空不报错。直接返回 null
        if (obj == null)
        {
            return null;
        }
        Validate.notBlank(methodName, "methodName can't be blank");
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
        {
            try
            {
                Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
                makeAccessible(method);
                return method;
            }
            catch (NoSuchMethodException e)
            {
                continue;
            }
        }
        return null;
    }
    /**
     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
     * 如向上转型到Object仍无法找到, 返回null.
     * 只匹配函数名。
     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
    {
        // 为空不报错。直接返回 null
        if (obj == null)
        {
            return null;
        }
        Validate.notBlank(methodName, "methodName can't be blank");
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
        {
            Method[] methods = searchType.getDeclaredMethods();
            for (Method method : methods)
            {
                if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
                {
                    makeAccessible(method);
                    return method;
                }
            }
        }
        return null;
    }
    /**
     * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Method method)
    {
        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
                && !method.isAccessible())
        {
            method.setAccessible(true);
        }
    }
    /**
     * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Field field)
    {
        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
                || Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
        {
            field.setAccessible(true);
        }
    }
    /**
     * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
     * 如无法找到, 返回Object.class.
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> getClassGenricType(final Class clazz)
    {
        return getClassGenricType(clazz, 0);
    }
    /**
     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
     * 如无法找到, 返回Object.class.
     */
    public static Class getClassGenricType(final Class clazz, final int index)
    {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType))
        {
            logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0)
        {
            logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class))
        {
            logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }
        return (Class) params[index];
    }
    public static Class<?> getUserClass(Object instance)
    {
        if (instance == null)
        {
            throw new RuntimeException("Instance must not be null");
        }
        Class clazz = instance.getClass();
        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
        {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !Object.class.equals(superClass))
            {
                return superClass;
            }
        }
        return clazz;
    }
    /**
     * 将反射时的checked exception转换为unchecked exception.
     */
    public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
    {
        if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
                || e instanceof NoSuchMethodException)
        {
            return new IllegalArgumentException(msg, e);
        }
        else if (e instanceof InvocationTargetException)
        {
            return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
        }
        return new RuntimeException(msg, e);
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java
New file
@@ -0,0 +1,291 @@
package com.ruoyi.common.utils.sign;
/**
 * Base64工具类
 *
 * @author ruoyi
 */
public final class Base64
{
    static private final int     BASELENGTH           = 128;
    static private final int     LOOKUPLENGTH         = 64;
    static private final int     TWENTYFOURBITGROUP   = 24;
    static private final int     EIGHTBIT             = 8;
    static private final int     SIXTEENBIT           = 16;
    static private final int     FOURBYTE             = 4;
    static private final int     SIGN                 = -128;
    static private final char    PAD                  = '=';
    static final private byte[]  base64Alphabet       = new byte[BASELENGTH];
    static final private char[]  lookUpBase64Alphabet = new char[LOOKUPLENGTH];
    static
    {
        for (int i = 0; i < BASELENGTH; ++i)
        {
            base64Alphabet[i] = -1;
        }
        for (int i = 'Z'; i >= 'A'; i--)
        {
            base64Alphabet[i] = (byte) (i - 'A');
        }
        for (int i = 'z'; i >= 'a'; i--)
        {
            base64Alphabet[i] = (byte) (i - 'a' + 26);
        }
        for (int i = '9'; i >= '0'; i--)
        {
            base64Alphabet[i] = (byte) (i - '0' + 52);
        }
        base64Alphabet['+'] = 62;
        base64Alphabet['/'] = 63;
        for (int i = 0; i <= 25; i++)
        {
            lookUpBase64Alphabet[i] = (char) ('A' + i);
        }
        for (int i = 26, j = 0; i <= 51; i++, j++)
        {
            lookUpBase64Alphabet[i] = (char) ('a' + j);
        }
        for (int i = 52, j = 0; i <= 61; i++, j++)
        {
            lookUpBase64Alphabet[i] = (char) ('0' + j);
        }
        lookUpBase64Alphabet[62] = (char) '+';
        lookUpBase64Alphabet[63] = (char) '/';
    }
    private static boolean isWhiteSpace(char octect)
    {
        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
    }
    private static boolean isPad(char octect)
    {
        return (octect == PAD);
    }
    private static boolean isData(char octect)
    {
        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
    }
    /**
     * Encodes hex octects into Base64
     *
     * @param binaryData Array containing binaryData
     * @return Encoded Base64 array
     */
    public static String encode(byte[] binaryData)
    {
        if (binaryData == null)
        {
            return null;
        }
        int lengthDataBits = binaryData.length * EIGHTBIT;
        if (lengthDataBits == 0)
        {
            return "";
        }
        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
        int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
        char encodedData[] = null;
        encodedData = new char[numberQuartet * 4];
        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
        int encodedIndex = 0;
        int dataIndex = 0;
        for (int i = 0; i < numberTriplets; i++)
        {
            b1 = binaryData[dataIndex++];
            b2 = binaryData[dataIndex++];
            b3 = binaryData[dataIndex++];
            l = (byte) (b2 & 0x0f);
            k = (byte) (b1 & 0x03);
            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
            byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
        }
        // form integral number of 6-bit groups
        if (fewerThan24bits == EIGHTBIT)
        {
            b1 = binaryData[dataIndex];
            k = (byte) (b1 & 0x03);
            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
            encodedData[encodedIndex++] = PAD;
            encodedData[encodedIndex++] = PAD;
        }
        else if (fewerThan24bits == SIXTEENBIT)
        {
            b1 = binaryData[dataIndex];
            b2 = binaryData[dataIndex + 1];
            l = (byte) (b2 & 0x0f);
            k = (byte) (b1 & 0x03);
            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
            encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
            encodedData[encodedIndex++] = PAD;
        }
        return new String(encodedData);
    }
    /**
     * Decodes Base64 data into octects
     *
     * @param encoded string containing Base64 data
     * @return Array containind decoded data.
     */
    public static byte[] decode(String encoded)
    {
        if (encoded == null)
        {
            return null;
        }
        char[] base64Data = encoded.toCharArray();
        // remove white spaces
        int len = removeWhiteSpace(base64Data);
        if (len % FOURBYTE != 0)
        {
            return null;// should be divisible by four
        }
        int numberQuadruple = (len / FOURBYTE);
        if (numberQuadruple == 0)
        {
            return new byte[0];
        }
        byte decodedData[] = null;
        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
        int i = 0;
        int encodedIndex = 0;
        int dataIndex = 0;
        decodedData = new byte[(numberQuadruple) * 3];
        for (; i < numberQuadruple - 1; i++)
        {
            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
                    || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++])))
            {
                return null;
            } // if found "no data" just return null
            b1 = base64Alphabet[d1];
            b2 = base64Alphabet[d2];
            b3 = base64Alphabet[d3];
            b4 = base64Alphabet[d4];
            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
        }
        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])))
        {
            return null;// if found "no data" just return null
        }
        b1 = base64Alphabet[d1];
        b2 = base64Alphabet[d2];
        d3 = base64Data[dataIndex++];
        d4 = base64Data[dataIndex++];
        if (!isData((d3)) || !isData((d4)))
        {// Check if they are PAD characters
            if (isPad(d3) && isPad(d4))
            {
                if ((b2 & 0xf) != 0)// last 4 bits should be zero
                {
                    return null;
                }
                byte[] tmp = new byte[i * 3 + 1];
                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
                return tmp;
            }
            else if (!isPad(d3) && isPad(d4))
            {
                b3 = base64Alphabet[d3];
                if ((b3 & 0x3) != 0)// last 2 bits should be zero
                {
                    return null;
                }
                byte[] tmp = new byte[i * 3 + 2];
                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
                return tmp;
            }
            else
            {
                return null;
            }
        }
        else
        { // No PAD e.g 3cQl
            b3 = base64Alphabet[d3];
            b4 = base64Alphabet[d4];
            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
        }
        return decodedData;
    }
    /**
     * remove WhiteSpace from MIME containing encoded Base64 data.
     *
     * @param data the byte array of base64 data (with WS)
     * @return the new length
     */
    private static int removeWhiteSpace(char[] data)
    {
        if (data == null)
        {
            return 0;
        }
        // count characters that's not whitespace
        int newSize = 0;
        int len = data.length;
        for (int i = 0; i < len; i++)
        {
            if (!isWhiteSpace(data[i]))
            {
                data[newSize++] = data[i];
            }
        }
        return newSize;
    }
}
pt-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java
New file
@@ -0,0 +1,67 @@
package com.ruoyi.common.utils.sign;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * Md5加密方法
 *
 * @author ruoyi
 */
public class Md5Utils
{
    private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
    private static byte[] md5(String s)
    {
        MessageDigest algorithm;
        try
        {
            algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(s.getBytes("UTF-8"));
            byte[] messageDigest = algorithm.digest();
            return messageDigest;
        }
        catch (Exception e)
        {
            log.error("MD5 Error...", e);
        }
        return null;
    }
    private static final String toHex(byte hash[])
    {
        if (hash == null)
        {
            return null;
        }
        StringBuffer buf = new StringBuffer(hash.length * 2);
        int i;
        for (i = 0; i < hash.length; i++)
        {
            if ((hash[i] & 0xff) < 0x10)
            {
                buf.append("0");
            }
            buf.append(Long.toString(hash[i] & 0xff, 16));
        }
        return buf.toString();
    }
    public static String hash(String s)
    {
        try
        {
            return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
        }
        catch (Exception e)
        {
            log.error("not supported charset...{}", e);
            return s;
        }
    }
}
Diff truncated after the above file
pt-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java pt-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java pt-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java pt-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java pt-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java pt-common/src/main/java/com/ruoyi/common/xss/Xss.java pt-common/src/main/java/com/ruoyi/common/xss/XssValidator.java pt-common/target/maven-archiver/pom.properties pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst pt-errand/pom.xml pt-errand/src/main/java/com/ruoyi/errand/constant/AppUserStatusConstant.java pt-errand/src/main/java/com/ruoyi/errand/constant/DelFlagConstant.java pt-errand/src/main/java/com/ruoyi/errand/constant/IsDefaultConstant.java pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstLoginConstant.java pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstOrder.java pt-errand/src/main/java/com/ruoyi/errand/constant/JwtClaimsConstant.java pt-errand/src/main/java/com/ruoyi/errand/constant/MessageConstant.java pt-errand/src/main/java/com/ruoyi/errand/constant/StatusConstant.java pt-errand/src/main/java/com/ruoyi/errand/constant/SystemConfigTypeConstant.java pt-errand/src/main/java/com/ruoyi/errand/context/BaseContext.java pt-errand/src/main/java/com/ruoyi/errand/domain/AddressBook.java pt-errand/src/main/java/com/ruoyi/errand/domain/Agreement.java pt-errand/src/main/java/com/ruoyi/errand/domain/AppUser.java pt-errand/src/main/java/com/ruoyi/errand/domain/Banner.java pt-errand/src/main/java/com/ruoyi/errand/domain/Community.java pt-errand/src/main/java/com/ruoyi/errand/domain/CommunityCourier.java pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java pt-errand/src/main/java/com/ruoyi/errand/domain/Feedback.java pt-errand/src/main/java/com/ruoyi/errand/domain/Order.java pt-errand/src/main/java/com/ruoyi/errand/domain/Phone.java pt-errand/src/main/java/com/ruoyi/errand/domain/Region.java pt-errand/src/main/java/com/ruoyi/errand/domain/Report.java pt-errand/src/main/java/com/ruoyi/errand/domain/SystemConfig.java pt-errand/src/main/java/com/ruoyi/errand/domain/UserCancellationLog.java pt-errand/src/main/java/com/ruoyi/errand/domain/VipOrder.java pt-errand/src/main/java/com/ruoyi/errand/domain/VipSetting.java pt-errand/src/main/java/com/ruoyi/errand/interceptor/APPJwtTokenInterceptor.java pt-errand/src/main/java/com/ruoyi/errand/mapper/AddressBookMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/AgreementMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/AppUserMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/BannerMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityCourierMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/CourierMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/EvaluationMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/FeedbackMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/OrderMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/PhoneMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/RegionMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/ReportMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/SystemConfigMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/UserCancellationLogMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/VipOrderMapper.java pt-errand/src/main/java/com/ruoyi/errand/mapper/VipSettingMapper.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddAddressBookDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddReportDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AgreementDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AppletLogin.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/BirthDayDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/ConfirmOrderDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/MobileLoginDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderPayment.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsVO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/RegisterDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/SetConfirmOrderDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/StartPageSetDto.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/UpdateAddressBookDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/VipPaymentDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddBannerDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCommunityDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AppUserPageListDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CommunityPageListDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CourierPageListDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCommunityDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCourierDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FeedbackPageListDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FinanceStatisticsDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/OrderPageListDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/ReportPageListDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/SetPriceDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/UserStatsDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookByCommunityIdVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserInfoVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserOrderListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/BannerVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CommunityListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CompleteOrderDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/ConfirmOrderVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierInfoVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierOrderListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierStatisticsVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderDetailVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderPageVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderTopInfoVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/UserTopInfoVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/VipInfoListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/login/LoginVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCommunityListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCourierListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserPageListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserSysDetailVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerDetailVo.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerPageListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunityPageListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunitySysDetailVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierPageListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/EditBannerDTO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FeedbackPageListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderSysDetailVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/RefundVipOrderVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/ReportPageListVO.java pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/UserStatsVO.java pt-errand/src/main/java/com/ruoyi/errand/service/AddressBookService.java pt-errand/src/main/java/com/ruoyi/errand/service/AgreementService.java pt-errand/src/main/java/com/ruoyi/errand/service/AppUserService.java pt-errand/src/main/java/com/ruoyi/errand/service/BannerService.java pt-errand/src/main/java/com/ruoyi/errand/service/CommunityCourierService.java pt-errand/src/main/java/com/ruoyi/errand/service/CommunityService.java pt-errand/src/main/java/com/ruoyi/errand/service/CourierService.java pt-errand/src/main/java/com/ruoyi/errand/service/EvaluationService.java pt-errand/src/main/java/com/ruoyi/errand/service/FeedbackService.java pt-errand/src/main/java/com/ruoyi/errand/service/OrderService.java pt-errand/src/main/java/com/ruoyi/errand/service/PhoneService.java pt-errand/src/main/java/com/ruoyi/errand/service/RegionService.java pt-errand/src/main/java/com/ruoyi/errand/service/ReportService.java pt-errand/src/main/java/com/ruoyi/errand/service/SystemConfigService.java pt-errand/src/main/java/com/ruoyi/errand/service/UserCancellationLogService.java pt-errand/src/main/java/com/ruoyi/errand/service/VipOrderService.java pt-errand/src/main/java/com/ruoyi/errand/service/VipSettingService.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/AddressBookServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/AgreementServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/BannerServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityCourierServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/CourierServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/FeedbackServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/PhoneServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/RegionServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/ReportServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/SystemConfigServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/UserCancellationLogServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipOrderServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipSettingServiceImpl.java pt-errand/src/main/java/com/ruoyi/errand/utils/AES.java pt-errand/src/main/java/com/ruoyi/errand/utils/BatchNumberUtils.java pt-errand/src/main/java/com/ruoyi/errand/utils/CloseOrderResult.java pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java pt-errand/src/main/java/com/ruoyi/errand/utils/FrpCodeEnum.java pt-errand/src/main/java/com/ruoyi/errand/utils/JwtUtil.java pt-errand/src/main/java/com/ruoyi/errand/utils/MD5AndKL.java pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentCycleHelper.java pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentUtil.java pt-errand/src/main/java/com/ruoyi/errand/utils/QueryOrderResult.java pt-errand/src/main/java/com/ruoyi/errand/utils/QueryRefundResult.java pt-errand/src/main/java/com/ruoyi/errand/utils/RedisService.java pt-errand/src/main/java/com/ruoyi/errand/utils/RefundCallbackResult.java pt-errand/src/main/java/com/ruoyi/errand/utils/RefundResult.java pt-errand/src/main/java/com/ruoyi/errand/utils/SMSUtil.java pt-errand/src/main/java/com/ruoyi/errand/utils/TaskUtil.java pt-errand/src/main/java/com/ruoyi/errand/utils/TokenBlacklistService.java pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayCallbackResult.java pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayResult.java pt-errand/src/main/java/com/ruoyi/errand/utils/WXCore.java pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppAuthenticationToken.java pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppSecurityProperties.java pt-errand/src/main/java/com/ruoyi/errand/utils/WeChatUtil.java pt-errand/src/main/java/com/ruoyi/errand/utils/WebSocketConfig.java pt-errand/src/main/java/com/ruoyi/errand/utils/WxPKCS7Encoder.java pt-errand/src/main/resources/mapper/AddressBookMapper.xml pt-errand/src/main/resources/mapper/AgreementMapper.xml pt-errand/src/main/resources/mapper/AppUserMapper.xml pt-errand/src/main/resources/mapper/BannerMapper.xml pt-errand/src/main/resources/mapper/CommunityCourierMapper.xml pt-errand/src/main/resources/mapper/CommunityMapper.xml pt-errand/src/main/resources/mapper/CourierMapper.xml pt-errand/src/main/resources/mapper/EvaluationMapper.xml pt-errand/src/main/resources/mapper/FeedbackMapper.xml pt-errand/src/main/resources/mapper/OrderMapper.xml pt-errand/src/main/resources/mapper/PhoneMapper.xml pt-errand/src/main/resources/mapper/RegionMapper.xml pt-errand/src/main/resources/mapper/ReportMapper.xml pt-errand/src/main/resources/mapper/SystemConfigMapper.xml pt-errand/src/main/resources/mapper/UserCancellationLogMapper.xml pt-errand/src/main/resources/mapper/VipOrderMapper.xml pt-errand/src/main/resources/mapper/VipSettingMapper.xml pt-errand/src/main/resources/templates/bjd.xlsx pt-errand/target/classes/mapper/AddressBookMapper.xml pt-errand/target/classes/mapper/AgreementMapper.xml pt-errand/target/classes/mapper/AppUserMapper.xml pt-errand/target/classes/mapper/BannerMapper.xml pt-errand/target/classes/mapper/CommunityCourierMapper.xml pt-errand/target/classes/mapper/CommunityMapper.xml pt-errand/target/classes/mapper/CourierMapper.xml pt-errand/target/classes/mapper/EvaluationMapper.xml pt-errand/target/classes/mapper/FeedbackMapper.xml pt-errand/target/classes/mapper/OrderMapper.xml pt-errand/target/classes/mapper/PhoneMapper.xml pt-errand/target/classes/mapper/RegionMapper.xml pt-errand/target/classes/mapper/ReportMapper.xml pt-errand/target/classes/mapper/SystemConfigMapper.xml pt-errand/target/classes/mapper/UserCancellationLogMapper.xml pt-errand/target/classes/mapper/VipOrderMapper.xml pt-errand/target/classes/mapper/VipSettingMapper.xml pt-errand/target/classes/templates/bjd.xlsx pt-errand/target/maven-archiver/pom.properties pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst pt-framework/pom.xml pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java pt-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java pt-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java pt-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java pt-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java pt-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java pt-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java pt-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java pt-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java pt-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java pt-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java pt-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java pt-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java pt-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java pt-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java pt-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java pt-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java pt-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java pt-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java pt-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java pt-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java pt-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java pt-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java pt-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java pt-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java pt-framework/target/maven-archiver/pom.properties pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst pt-system/pom.xml pt-system/src/main/java/com/ruoyi/system/domain/SysCache.java pt-system/src/main/java/com/ruoyi/system/domain/SysConfig.java pt-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java pt-system/src/main/java/com/ruoyi/system/domain/SysNotice.java pt-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java pt-system/src/main/java/com/ruoyi/system/domain/SysPost.java pt-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java pt-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java pt-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java pt-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java pt-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java pt-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java pt-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java pt-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java pt-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysRoleDTO.java pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysUserDTO.java pt-system/src/main/java/com/ruoyi/system/object/dto/EditSysRoleDTO.java pt-system/src/main/java/com/ruoyi/system/object/dto/SetPasswordDTO.java pt-system/src/main/java/com/ruoyi/system/object/vo/EditSysUserDTO.java pt-system/src/main/java/com/ruoyi/system/object/vo/MenuTreeVO.java pt-system/src/main/java/com/ruoyi/system/object/vo/SysDeptPageVO.java pt-system/src/main/java/com/ruoyi/system/object/vo/SysRolePageVO.java pt-system/src/main/java/com/ruoyi/system/object/vo/SysRoleVO.java pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserPageListVO.java pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserVO.java pt-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java pt-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java pt-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java pt-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java pt-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java pt-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java pt-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java pt-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java pt-system/src/main/java/com/ruoyi/system/service/ISysPostService.java pt-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java pt-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java pt-system/src/main/java/com/ruoyi/system/service/ISysUserService.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java pt-system/src/main/resources/mapper/system/SysConfigMapper.xml pt-system/src/main/resources/mapper/system/SysDeptMapper.xml pt-system/src/main/resources/mapper/system/SysDictDataMapper.xml pt-system/src/main/resources/mapper/system/SysDictTypeMapper.xml pt-system/src/main/resources/mapper/system/SysLogininforMapper.xml pt-system/src/main/resources/mapper/system/SysMenuMapper.xml pt-system/src/main/resources/mapper/system/SysNoticeMapper.xml pt-system/src/main/resources/mapper/system/SysOperLogMapper.xml pt-system/src/main/resources/mapper/system/SysPostMapper.xml pt-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml pt-system/src/main/resources/mapper/system/SysRoleMapper.xml pt-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml pt-system/src/main/resources/mapper/system/SysUserMapper.xml pt-system/src/main/resources/mapper/system/SysUserPostMapper.xml pt-system/src/main/resources/mapper/system/SysUserRoleMapper.xml pt-system/target/classes/mapper/system/SysConfigMapper.xml pt-system/target/classes/mapper/system/SysDeptMapper.xml pt-system/target/classes/mapper/system/SysDictDataMapper.xml pt-system/target/classes/mapper/system/SysDictTypeMapper.xml pt-system/target/classes/mapper/system/SysLogininforMapper.xml pt-system/target/classes/mapper/system/SysMenuMapper.xml pt-system/target/classes/mapper/system/SysNoticeMapper.xml pt-system/target/classes/mapper/system/SysOperLogMapper.xml pt-system/target/classes/mapper/system/SysPostMapper.xml pt-system/target/classes/mapper/system/SysRoleDeptMapper.xml pt-system/target/classes/mapper/system/SysRoleMapper.xml pt-system/target/classes/mapper/system/SysRoleMenuMapper.xml pt-system/target/classes/mapper/system/SysUserMapper.xml pt-system/target/classes/mapper/system/SysUserPostMapper.xml pt-system/target/classes/mapper/system/SysUserRoleMapper.xml pt-system/target/maven-archiver/pom.properties pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst