From 5d7b65670282a4fad015e37d567cfa171b162052 Mon Sep 17 00:00:00 2001
From: huliguo <2023611923@qq.com>
Date: 星期二, 20 五月 2025 12:25:19 +0800
Subject: [PATCH] 基础代码

---
 pt-system/src/main/java/com/ruoyi/system/service/ISysPostService.java                              |   99 
 pt-errand/src/main/resources/mapper/FeedbackMapper.xml                                             |   23 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCourierDTO.java                        |   16 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunityPageListVO.java                    |   35 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CompleteOrderDTO.java                       |   29 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/BannerController.java                       |   97 
 pt-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java                                 |   19 
 pt-admin/target/classes/i18n/messages.properties                                                   |   38 
 pt-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java                            |   58 
 pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java               |   88 
 pt-errand/src/main/resources/mapper/CourierMapper.xml                                              |  103 
 pt-system/pom.xml                                                                                  |   28 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/ConfirmOrderDTO.java                       |   37 
 pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentUtil.java                                    |  285 
 pt-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java        |   16 
 pt-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java                  |  111 
 pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java                          |   84 
 pt-system/target/maven-archiver/pom.properties                                                     |    3 
 pt-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java                                |  125 
 pt-errand/src/main/java/com/ruoyi/errand/utils/CloseOrderResult.java                               |   41 
 pt-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java                          |  150 
 pt-common/src/main/java/com/ruoyi/common/core/domain/BaseResult.java                               |   48 
 pt-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java                         |   97 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/BirthDayDTO.java                           |   16 
 pt-system/src/main/java/com/ruoyi/system/object/vo/SysRoleVO.java                                  |   18 
 pt-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java                                   |   26 
 pt-admin/src/main/resources/mybatis/mybatis-config.xml                                             |   20 
 pt-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java                       |   30 
 pt-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java   |   28 
 pom.xml                                                                                            |  269 
 pt-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java                      |   24 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/RegionMapper.java                                  |    9 
 pt-errand/src/main/java/com/ruoyi/errand/domain/UserCancellationLog.java                           |   36 
 pt-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java           |  152 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/UserCancellationLogServiceImpl.java          |   12 
 pt-errand/src/main/resources/mapper/BannerMapper.xml                                               |   15 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerPageListVO.java                       |   19 
 pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppSecurityProperties.java                        |   15 
 pt-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java                                     |  106 
 pt-common/pom.xml                                                                                  |  183 
 pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java                              |  115 
 pt-errand/src/main/java/com/ruoyi/errand/domain/SystemConfig.java                                  |   26 
 pt-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java                              |  147 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/AddressBookController.java                  |   81 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/ReportServiceImpl.java                       |   67 
 pt-errand/src/main/resources/mapper/AddressBookMapper.xml                                          |   42 
 pt-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java                                   |  218 
 pt-system/src/main/java/com/ruoyi/system/object/dto/EditSysRoleDTO.java                            |   15 
 pt-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java                              |   43 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java                      |  178 
 pt-errand/src/main/java/com/ruoyi/errand/utils/JwtUtil.java                                        |   71 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Agreement.java                                     |   33 
 pt-common/src/main/java/com/ruoyi/common/utils/LogUtils.java                                       |   18 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/login/LoginVO.java                              |   23 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java                                 |  129 
 pt-errand/src/main/java/com/ruoyi/errand/service/CourierService.java                               |   45 
 pt-common/src/main/java/com/ruoyi/common/core/domain/ResponseUtils.java                            |   77 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FinanceStatisticsDTO.java                  |   45 
 pt-system/src/main/resources/mapper/system/SysDeptMapper.xml                                       |  180 
 pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java                      |   16 
 pt-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml                                   |   47 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/SetConfirmOrderDTO.java                    |   29 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java                             |   95 
 pt-system/src/main/resources/mapper/system/SysLogininforMapper.xml                                 |   57 
 pt-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java                      |   91 
 pt-errand/src/main/java/com/ruoyi/errand/service/CommunityService.java                             |   36 
 pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java                           |  240 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunitySysDetailVO.java                   |   45 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java                             |   68 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/UserCancellationLogMapper.java                     |   10 
 pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java                           |  203 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java                    |  122 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/PhoneServiceImpl.java                        |   55 
 pt-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java            |   52 
 pt-system/target/classes/mapper/system/SysDictTypeMapper.xml                                       |  105 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/OrderPageListDTO.java                      |   58 
 pt-errand/src/main/resources/mapper/CommunityCourierMapper.xml                                     |   18 
 pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst         |  102 
 pt-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java                    |  183 
 pt-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java                                  |   67 
 pt-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java                       |   11 
 pt-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java            |   56 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Banner.java                                        |   59 
 pt-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java                                |  167 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Report.java                                        |   58 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityServiceImpl.java                    |  271 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FeedbackPageListDTO.java                   |   23 
 pt-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java                                 |   19 
 pt-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java                                 |   20 
 pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java               |   72 
 pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java      |   45 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityMapper.java                               |   26 
 pt-errand/src/main/resources/templates/bjd.xlsx                                                    |    0 
 pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java                           |  274 
 pt-common/src/main/java/com/ruoyi/common/constant/SecurityConstants.java                           |   63 
 pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java                       |  176 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java                         |   30 
 pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java                                 |  110 
 pt-errand/src/main/java/com/ruoyi/errand/utils/TokenBlacklistService.java                          |   93 
 pt-errand/src/main/resources/mapper/AgreementMapper.xml                                            |    6 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java                              |   48 
 pt-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java                               |  124 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CommunityListVO.java                        |   17 
 pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst         |  114 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/VipSettingMapper.java                              |   14 
 pt-errand/src/main/java/com/ruoyi/errand/utils/BatchNumberUtils.java                               |  120 
 pt-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java                         |   88 
 pt-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java                                     |  382 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/AgreementServiceImpl.java                    |   17 
 pt-system/src/main/java/com/ruoyi/system/object/vo/EditSysUserDTO.java                             |   15 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/AddressBookMapper.java                             |   17 
 pt-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java                                |  268 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java                                 |  138 
 pt-system/src/main/resources/mapper/system/SysUserPostMapper.xml                                   |   34 
 pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java                          |   66 
 pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserPageListVO.java                          |   24 
 pt-common/src/main/java/com/ruoyi/common/easyExcel/DateConverter.java                              |  130 
 pt-common/src/main/java/com/ruoyi/common/exception/GlobalException.java                            |   58 
 pt-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java                       |  114 
 pt-admin/target/classes/application-druid.yml                                                      |   61 
 pt-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java                                 |   24 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/UpdateAddressBookDTO.java                  |   17 
 pt-common/src/main/java/com/ruoyi/common/exception/UtilException.java                              |   26 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java                                    |   49 
 pt-common/src/main/java/com/ruoyi/common/enums/UserStatus.java                                     |   30 
 pt-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java                    |   16 
 pt-system/target/classes/mapper/system/SysDeptMapper.xml                                           |  180 
 pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java                          |  101 
 pt-admin/src/main/resources/i18n/messages.properties                                               |   38 
 pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysRoleDTO.java                             |   16 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderTopInfoVO.java                         |   39 
 pt-admin/target/classes/banner.txt                                                                 |    2 
 pt-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java                          |   15 
 pt-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java                      |   89 
 pt-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java        |  110 
 pt-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java                       |  202 
 pt-system/target/classes/mapper/system/SysUserPostMapper.xml                                       |   34 
 pt-admin/src/main/resources/logback.xml                                                            |   93 
 pt-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java                               |   92 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookByCommunityIdVO.java             |   29 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java                |   96 
 pt-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java                                  |  161 
 pt-errand/src/main/java/com/ruoyi/errand/service/RegionService.java                                |    7 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java                           |   42 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/EditBannerDTO.java                          |   18 
 pt-system/target/classes/mapper/system/SysConfigMapper.xml                                         |  117 
 pt-errand/src/main/java/com/ruoyi/errand/constant/SystemConfigTypeConstant.java                    |    8 
 pt-errand/src/main/java/com/ruoyi/errand/service/AgreementService.java                             |    8 
 pt-errand/src/main/java/com/ruoyi/errand/utils/WXCore.java                                         |   48 
 pt-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java                               |   56 
 pt-errand/src/main/java/com/ruoyi/errand/utils/RedisService.java                                   |  276 
 pt-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java             |   80 
 pt-errand/target/classes/mapper/RegionMapper.xml                                                   |    6 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AgreementDTO.java                          |   16 
 .idea/jarRepositories.xml                                                                          |   25 
 pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayCallbackResult.java                           |   89 
 pt-errand/src/main/java/com/ruoyi/errand/utils/TaskUtil.java                                       |   34 
 pt-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java            |   67 
 pt-system/src/main/resources/mapper/system/SysConfigMapper.xml                                     |  117 
 pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java                           |  325 
 pt-errand/target/classes/mapper/UserCancellationLogMapper.xml                                      |    6 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/UserCancellationLogController.java          |   19 
 pt-system/target/classes/mapper/system/SysUserRoleMapper.xml                                       |   51 
 pt-system/src/main/resources/mapper/system/SysNoticeMapper.xml                                     |   89 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java                    |   89 
 pt-system/src/main/resources/mapper/system/SysDictDataMapper.xml                                   |  124 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipSettingController.java                   |   61 
 pt-common/src/main/java/com/ruoyi/common/constant/UserConstants.java                               |   81 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java                |   65 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/SetPriceDTO.java                           |   22 
 pt-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java                             |  126 
 pt-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java                          |   62 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java                             |   83 
 pt-errand/src/main/java/com/ruoyi/errand/service/BannerService.java                                |   27 
 pt-errand/src/main/java/com/ruoyi/errand/domain/AddressBook.java                                   |   59 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddAddressBookDTO.java                     |   18 
 pt-common/src/main/java/com/ruoyi/common/exception/file/FileException.java                         |   19 
 pt-errand/target/classes/mapper/EvaluationMapper.xml                                               |   16 
 pt-errand/target/classes/mapper/AppUserMapper.xml                                                  |  132 
 pt-errand/src/main/resources/mapper/RegionMapper.xml                                               |    6 
 pt-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java                 |  115 
 pt-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java                                  |   39 
 pt-errand/src/main/resources/mapper/UserCancellationLogMapper.xml                                  |    6 
 pt-system/target/classes/mapper/system/SysDictDataMapper.xml                                       |  124 
 pt-errand/src/main/java/com/ruoyi/errand/service/ReportService.java                                |   20 
 pt-common/src/main/java/com/ruoyi/common/annotation/DataScope.java                                 |   33 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/AgreementMapper.java                               |    9 
 pt-common/src/main/java/com/ruoyi/common/exception/user/UserException.java                         |   18 
 pt-errand/target/classes/mapper/AgreementMapper.xml                                                |    6 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java                      |  598 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java                  |   35 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java                      |   31 
 pt-errand/src/main/java/com/ruoyi/errand/domain/AppUser.java                                       |   98 
 pt-errand/src/main/java/com/ruoyi/errand/utils/QueryRefundResult.java                              |   58 
 pt-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java                               |   59 
 pt-errand/target/classes/templates/bjd.xlsx                                                        |    0 
 pt-common/src/main/java/com/ruoyi/common/annotation/DataSource.java                                |   28 
 pt-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java                              |   85 
 pt-system/target/classes/mapper/system/SysNoticeMapper.xml                                         |   89 
 pt-errand/src/main/java/com/ruoyi/errand/utils/RefundCallbackResult.java                           |   59 
 pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java                        |   24 
 pt-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java                   |   61 
 pt-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java                                  |   94 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserPageListVO.java                      |   38 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java                   |  127 
 pt-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java                           |   83 
 pt-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java                          |   98 
 pt-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java                              |  256 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java                        |  607 
 pt-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java                              |   31 
 pt-system/src/main/java/com/ruoyi/system/object/vo/SysRolePageVO.java                              |   18 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/PhoneController.java                        |   42 
 pt-common/src/main/java/com/ruoyi/common/filter/XssFilter.java                                     |   75 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/AppUserMapper.java                                 |   34 
 pt-common/src/main/java/com/ruoyi/common/enums/OperatorType.java                                   |   24 
 pt-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java                       |  150 
 pt-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java                                   |   49 
 pt-errand/src/main/java/com/ruoyi/errand/domain/VipOrder.java                                      |   74 
 pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java                          |   61 
 pt-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java                   |  102 
 pt-errand/target/classes/mapper/VipSettingMapper.xml                                               |   12 
 pt-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java                                    |  291 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java                      |  498 
 pt-system/src/main/resources/mapper/system/SysPostMapper.xml                                       |  122 
 pt-errand/src/main/java/com/ruoyi/errand/service/CommunityCourierService.java                      |    7 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java                      |  314 
 pt-system/target/classes/mapper/system/SysPostMapper.xml                                           |  122 
 pt-errand/target/classes/mapper/CommunityCourierMapper.xml                                         |   18 
 pt-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java                      |   68 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FeedbackPageListVO.java                     |   30 
 pt-system/target/classes/mapper/system/SysOperLogMapper.xml                                        |   87 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/RegionController.java                       |   45 
 pt-errand/src/main/java/com/ruoyi/errand/utils/WeChatUtil.java                                     |  244 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipOrderServiceImpl.java                     |  181 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CourierPageListDTO.java                    |   27 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java                      |  369 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/ReportController.java                       |   73 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddReportDTO.java                          |   23 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipSettingServiceImpl.java                   |   40 
 pt-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java                                 |  144 
 pt-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml                                   |   34 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsVO.java                          |   20 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java                                 |  114 
 pt-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java                             |   76 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java                             |   44 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java                  |  223 
 pt-errand/src/main/java/com/ruoyi/errand/constant/JwtClaimsConstant.java                           |    7 
 pt-errand/src/main/java/com/ruoyi/errand/service/VipSettingService.java                            |   15 
 pt-errand/src/main/resources/mapper/PhoneMapper.xml                                                |    6 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AppletLogin.java                           |   16 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java                  |  120 
 pt-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java              |   89 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierStatisticsVO.java                    |   19 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipOrderController.java                     |   62 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java                      |  140 
 pt-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java                               |   79 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AppUserPageListDTO.java                    |   28 
 pt-errand/target/maven-archiver/pom.properties                                                     |    3 
 pt-errand/src/main/java/com/ruoyi/errand/domain/VipSetting.java                                    |   39 
 pt-errand/src/main/java/com/ruoyi/errand/constant/DelFlagConstant.java                             |    9 
 pt-system/src/main/java/com/ruoyi/system/object/vo/SysDeptPageVO.java                              |   17 
 pt-system/src/main/resources/mapper/system/SysUserRoleMapper.xml                                   |   51 
 pt-framework/pom.xml                                                                               |   68 
 pt-admin/target/classes/logback.xml                                                                |   93 
 pt-admin/src/main/resources/banner.txt                                                             |    2 
 pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java                          |  266 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderSysDetailVO.java                       |   81 
 pt-errand/src/main/java/com/ruoyi/errand/service/AppUserService.java                               |   58 
 pt-system/target/classes/mapper/system/SysUserMapper.xml                                           |  268 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/BannerServiceImpl.java                       |   80 
 pt-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java                        |   39 
 pt-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java       |   53 
 .idea/dataSources.xml                                                                              |   17 
 pt-errand/src/main/java/com/ruoyi/errand/domain/CommunityCourier.java                              |   40 
 .idea/misc.xml                                                                                     |   16 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddBannerDTO.java                          |   17 
 pt-errand/src/main/resources/mapper/VipSettingMapper.xml                                           |   12 
 pt-admin/pom.xml                                                                                   |   94 
 pt-errand/target/classes/mapper/AddressBookMapper.xml                                              |   42 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java                      |  115 
 pt-errand/src/main/java/com/ruoyi/errand/utils/QueryOrderResult.java                               |   83 
 pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java                       |   96 
 pt-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java                                 |  104 
 pt-errand/src/main/java/com/ruoyi/errand/interceptor/APPJwtTokenInterceptor.java                   |  126 
 pt-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java                        |   63 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityCourierController.java             |   19 
 pt-admin/src/main/java/com/ruoyi/PaoTuiServletInitializer.java                                     |   14 
 pt-common/target/maven-archiver/pom.properties                                                     |    3 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java                      |  682 
 pt-errand/target/classes/mapper/PhoneMapper.xml                                                    |    6 
 pt-errand/src/main/java/com/ruoyi/errand/utils/WebSocketConfig.java                                |   28 
 pt-system/target/classes/mapper/system/SysRoleDeptMapper.xml                                       |   34 
 pt-errand/src/main/java/com/ruoyi/errand/service/SystemConfigService.java                          |    7 
 pt-common/src/main/java/com/ruoyi/common/core/domain/R.java                                        |  115 
 pt-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java                           |   50 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserOrderListVO.java                     |   60 
 pt-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java                  |  137 
 pt-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java                      |   76 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/OrderMapper.java                                   |   43 
 pt-errand/src/main/java/com/ruoyi/errand/service/OrderService.java                                 |   59 
 pt-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java                                   |   46 
 pt-admin/src/main/java/com/ruoyi/PaoTuiApplication.java                                            |   22 
 pt-common/src/main/java/com/ruoyi/common/easyExcel/StringConverter.java                            |   62 
 pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppAuthenticationToken.java                       |   29 
 pt-common/src/main/java/com/ruoyi/common/exception/ServiceException.java                           |   74 
 pt-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java                           |  410 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/CourierMapper.java                                 |   32 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/VipPaymentDTO.java                         |   15 
 pt-errand/target/classes/mapper/SystemConfigMapper.xml                                             |    6 
 pt-system/src/main/java/com/ruoyi/system/domain/SysNotice.java                                     |  125 
 pt-common/src/main/java/com/ruoyi/common/utils/PageUtils.java                                      |   35 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java                                       |   63 
 pt-errand/src/main/java/com/ruoyi/errand/constant/StatusConstant.java                              |   15 
 pt-system/target/classes/mapper/system/SysRoleMenuMapper.xml                                       |   47 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java                     |   29 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java                             |   49 
 pt-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java                                |   55 
 pt-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java       |   27 
 pt-common/src/main/java/com/ruoyi/common/constant/TokenConstants.java                              |   35 
 pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayResult.java                                   |   73 
 pt-common/src/main/java/com/ruoyi/common/utils/StringUtils.java                                    |  710 
 pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java                                  | 1900 ++
 pt-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java                            |   32 
 pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java                          |  130 
 pt-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java                              |  189 
 pt-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java                |   16 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/PhoneMapper.java                                   |    9 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCommunityDTO.java                      |   18 
 pt-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java                             |  164 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsDTO.java                         |   28 
 pt-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java                       |  233 
 pt-framework/target/maven-archiver/pom.properties                                                  |    3 
 pt-common/src/main/java/com/ruoyi/common/xss/XssValidator.java                                     |   39 
 pt-errand/src/main/resources/mapper/VipOrderMapper.xml                                             |   11 
 pt-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java                                      |  484 
 pt-system/src/main/resources/mapper/system/SysRoleMapper.xml                                       |  160 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java                                 |  131 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/VipOrderMapper.java                                |   12 
 pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java                       |   72 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/OrderController.java                        |  299 
 .idea/encodings.xml                                                                                |   17 
 pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst       |  118 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java                      |  133 
 pt-errand/src/main/resources/mapper/OrderMapper.xml                                                |  426 
 pt-errand/src/main/resources/mapper/AppUserMapper.xml                                              |  132 
 pt-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java                              |   55 
 pt-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java                                   |  148 
 pt-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java                                    |  269 
 pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentCycleHelper.java                             |  200 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/AppUserController.java                      |  317 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Phone.java                                         |   31 
 pt-errand/src/main/java/com/ruoyi/errand/utils/MD5AndKL.java                                       |  112 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/RefundVipOrderVO.java                       |    5 
 pt-system/src/main/java/com/ruoyi/system/domain/SysConfig.java                                     |  111 
 pt-common/src/main/java/com/ruoyi/common/enums/LimitType.java                                      |   20 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/ConfirmOrderVO.java                         |   44 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java                      |  557 
 pt-common/src/main/java/com/ruoyi/common/constant/GenConstants.java                                |  117 
 pt-common/src/main/java/com/ruoyi/common/utils/Arith.java                                          |  113 
 pt-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java                                   |   46 
 pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserVO.java                                  |   16 
 pt-errand/src/main/java/com/ruoyi/errand/service/AddressBookService.java                           |   24 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java                                 |   99 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/CourierController.java                      |  156 
 pt-system/src/main/java/com/ruoyi/system/object/dto/SetPasswordDTO.java                            |   15 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java                      |  238 
 pt-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java                            |   89 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCommunityListVO.java                     |   15 
 pt-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java                                 |  367 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderDetailVO.java                          |   76 
 pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst       |   53 
 pt-common/src/main/java/com/ruoyi/common/exception/BaseException.java                              |   15 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java                    |   41 
 pt-admin/src/main/resources/application.yml                                                        |  209 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/RegionServiceImpl.java                       |   12 
 pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst      |   45 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Order.java                                         |  135 
 pt-errand/src/main/java/com/ruoyi/errand/constant/MessageConstant.java                             |   20 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCourierListVO.java                       |   19 
 pt-errand/src/main/java/com/ruoyi/errand/constant/IsDefaultConstant.java                           |    9 
 pt-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java                           |  232 
 .idea/vcs.xml                                                                                      |    6 
 pt-errand/src/main/java/com/ruoyi/errand/utils/RefundResult.java                                   |   55 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/BannerVO.java                               |   17 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java                     |   44 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/UserTopInfoVO.java                          |   14 
 pt-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java                               |   40 
 pt-admin/src/main/resources/application-druid.yml                                                  |   61 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/FeedbackMapper.java                                |   14 
 pt-errand/target/classes/mapper/CommunityMapper.xml                                                |   68 
 pt-common/src/main/java/com/ruoyi/common/constant/Constants.java                                   |  173 
 pt-admin/src/main/resources/META-INF/spring-devtools.properties                                    |    1 
 pt-errand/src/main/java/com/ruoyi/errand/service/FeedbackService.java                              |   19 
 pt-errand/target/classes/mapper/BannerMapper.xml                                                   |   15 
 pt-common/src/main/java/com/ruoyi/common/utils/DictUtils.java                                      |  239 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/AddressBookServiceImpl.java                  |  130 
 pt-system/target/classes/mapper/system/SysRoleMapper.xml                                           |  160 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/FeedbackController.java                     |   71 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/CourierServiceImpl.java                      |  380 
 pt-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java                               |   49 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java                    |  108 
 pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java                 |   86 
 pt-system/src/main/resources/mapper/system/SysUserMapper.xml                                       |  268 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/RegisterDTO.java                           |   18 
 pt-errand/src/main/resources/mapper/SystemConfigMapper.xml                                         |    6 
 pt-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java                                   |  122 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/VipInfoListVO.java                          |   22 
 pt-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java                                       |   86 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/EvaluationController.java                   |   36 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Feedback.java                                      |   63 
 pt-errand/target/classes/mapper/ReportMapper.xml                                                   |   39 
 pt-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java                               |   93 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/ReportPageListDTO.java                     |   31 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/FeedbackServiceImpl.java                     |   65 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/MobileLoginDTO.java                        |   21 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/EvaluationMapper.java                              |   11 
 pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java               |   77 
 pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java                        |  184 
 pt-errand/src/main/resources/mapper/CommunityMapper.xml                                            |   68 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityCourierServiceImpl.java             |   12 
 pt-errand/src/main/java/com/ruoyi/errand/service/PhoneService.java                                 |   10 
 pt-admin/target/classes/application.yml                                                            |  209 
 pt-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java                                 |  113 
 pt-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java                                 |  293 
 pt-admin/target/classes/mybatis/mybatis-config.xml                                                 |   20 
 pt-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java                                    |   70 
 pt-errand/src/main/java/com/ruoyi/errand/service/VipOrderService.java                              |   16 
 pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java                |   16 
 pt-common/src/main/java/com/ruoyi/common/utils/Threads.java                                        |   99 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityController.java                    |  129 
 pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java         |   16 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/UserStatsDTO.java                          |   28 
 pt-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java                            |   79 
 .idea/compiler.xml                                                                                 |   25 
 pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java                            |   24 
 pt-errand/target/classes/mapper/VipOrderMapper.xml                                                 |   11 
 pt-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java                                     |   36 
 pt-system/src/main/resources/mapper/system/SysOperLogMapper.xml                                    |   87 
 pt-errand/src/main/resources/mapper/EvaluationMapper.xml                                           |   16 
 pt-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java                             |   69 
 pt-errand/src/main/java/com/ruoyi/errand/context/BaseContext.java                                  |   20 
 pt-errand/src/main/java/com/ruoyi/errand/service/UserCancellationLogService.java                   |    7 
 pt-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java       |   73 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserSysDetailVO.java                     |   47 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java                     |  125 
 pt-errand/src/main/java/com/ruoyi/errand/utils/AES.java                                            |   72 
 pt-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java                              |  135 
 pt-system/src/main/resources/mapper/system/SysMenuMapper.xml                                       |  218 
 pt-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java                           |   48 
 pt-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java  |   16 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderPayment.java                          |   17 
 pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstLoginConstant.java                        |    9 
 pt-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java             |   66 
 pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java |   16 
 pt-common/src/main/java/com/ruoyi/common/annotation/Excel.java                                     |  197 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java                    |  232 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookListVO.java                      |   33 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerDetailVo.java                         |   23 
 pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java                   |   26 
 pt-system/src/main/java/com/ruoyi/system/domain/SysCache.java                                      |   81 
 pt-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java                                |   98 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/BannerMapper.java                                  |   21 
 pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java                  |   67 
 pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysUserDTO.java                             |   19 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/UserStatsVO.java                            |   18 
 pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java                  |  121 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/ReportPageListVO.java                       |   40 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/StartPageSetDto.java                       |   27 
 pt-errand/src/main/java/com/ruoyi/errand/utils/FrpCodeEnum.java                                    |   52 
 pt-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java                              |  240 
 pt-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java                        |   40 
 pt-common/src/main/java/com/ruoyi/common/core/text/Convert.java                                    | 1012 +
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierPageListVO.java                      |   35 
 pt-errand/target/classes/mapper/OrderMapper.xml                                                    |  426 
 pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java                      |  114 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java                             |   44 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/AgreementController.java                    |   51 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java                               |   62 
 pt-common/src/main/java/com/ruoyi/common/easyExcel/MultiDropdownWriteHandler.java                  |   50 
 pt-common/src/main/java/com/ruoyi/common/annotation/Log.java                                       |   51 
 pt-errand/src/main/java/com/ruoyi/errand/service/EvaluationService.java                            |    9 
 pt-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java                                   |   46 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Region.java                                        |   43 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java                  |  112 
 pt-system/src/main/java/com/ruoyi/system/domain/SysPost.java                                       |  124 
 pt-common/src/main/java/com/ruoyi/common/utils/DateUtils.java                                      |  191 
 pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst       |  129 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CommunityPageListDTO.java                  |   35 
 pt-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java                              |   52 
 pt-errand/src/main/java/com/ruoyi/errand/utils/SMSUtil.java                                        |  176 
 pt-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java                           |   55 
 pt-errand/target/classes/mapper/CourierMapper.xml                                                  |  103 
 pt-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java                               |   76 
 pt-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java                          |   34 
 pt-admin/src/main/java/com/ruoyi/web/controller/errand/SystemConfigController.java                 |   57 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/SystemConfigServiceImpl.java                 |   12 
 pt-errand/src/main/java/com/ruoyi/errand/utils/WxPKCS7Encoder.java                                 |   63 
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java                   |   58 
 pt-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java                      |   25 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderPageVO.java                            |   41 
 pt-system/src/main/java/com/ruoyi/system/object/vo/MenuTreeVO.java                                 |   30 
 pt-system/target/classes/mapper/system/SysLogininforMapper.xml                                     |   57 
 pt-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java                               |  204 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierInfoVO.java                          |   17 
 pt-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java                                 |   86 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/SystemConfigMapper.java                            |    9 
 pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCommunityDTO.java                       |   29 
 pt-system/src/main/resources/mapper/system/SysDictTypeMapper.xml                                   |  105 
 pt-system/target/classes/mapper/system/SysMenuMapper.xml                                           |  218 
 pt-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java                                |  570 
 pt-errand/src/main/java/com/ruoyi/errand/domain/Community.java                                     |   64 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java                        |   63 
 pt-common/src/main/java/com/ruoyi/common/easyExcel/BigDecimalConverter.java                        |   60 
 pt-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java                           |  132 
 pt-common/src/main/java/com/ruoyi/common/xss/Xss.java                                              |   27 
 pt-common/src/main/java/com/ruoyi/common/annotation/Excels.java                                    |   18 
 pt-errand/pom.xml                                                                                  |   48 
 pt-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java                                |   56 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityCourierMapper.java                        |   14 
 pt-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java                        |   48 
 pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst         |   53 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierOrderListVO.java                     |   37 
 pt-admin/src/main/java/com/ruoyi/web/controller/task/MessageTask.java                              |   11 
 pt-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java                   |   76 
 pt-errand/src/main/java/com/ruoyi/errand/constant/AppUserStatusConstant.java                       |   13 
 pt-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java   |   34 
 pt-common/src/main/java/com/ruoyi/common/easyExcel/NumberConverter.java                            |   57 
 pt-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java                             |   59 
 pt-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java   |   46 
 pt-errand/src/main/resources/mapper/ReportMapper.xml                                               |   39 
 pt-errand/src/main/java/com/ruoyi/errand/mapper/ReportMapper.java                                  |   15 
 pt-common/src/main/java/com/ruoyi/common/enums/BusinessType.java                                   |   59 
 pt-admin/target/classes/META-INF/spring-devtools.properties                                        |    1 
 pt-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java                                   |   46 
 pt-system/src/main/java/com/ruoyi/system/service/ISysUserService.java                              |  227 
 pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstOrder.java                                |    9 
 pt-errand/target/classes/mapper/FeedbackMapper.xml                                                 |   23 
 pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserInfoVO.java                          |   36 
 pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst    |   48 
 539 files changed, 47,208 insertions(+), 0 deletions(-)

diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..da95074
--- /dev/null
+++ b/.idea/compiler.xml
@@ -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>
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..12892b8
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -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>
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..b187773
--- /dev/null
+++ b/.idea/encodings.xml
@@ -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>
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..c48ef6b
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -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>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..e12de5b
--- /dev/null
+++ b/.idea/misc.xml
@@ -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>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6790856
--- /dev/null
+++ b/pom.xml
@@ -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>
\ No newline at end of file
diff --git a/pt-admin/pom.xml b/pt-admin/pom.xml
new file mode 100644
index 0000000..2bf76e9
--- /dev/null
+++ b/pt-admin/pom.xml
@@ -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>
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/PaoTuiApplication.java b/pt-admin/src/main/java/com/ruoyi/PaoTuiApplication.java
new file mode 100644
index 0000000..74e56bb
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/PaoTuiApplication.java
@@ -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("(♥◠‿◠)ノ゙ 启动成功   ლ(´ڡ`ლ)゙");
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/PaoTuiServletInitializer.java b/pt-admin/src/main/java/com/ruoyi/PaoTuiServletInitializer.java
new file mode 100644
index 0000000..19049cc
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/PaoTuiServletInitializer.java
@@ -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);
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
new file mode 100644
index 0000000..7ad295d
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
@@ -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;
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
new file mode 100644
index 0000000..2fc8b80
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
@@ -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);
+//        }
+//    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AddressBookController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AddressBookController.java
new file mode 100644
index 0000000..6f85b51
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AddressBookController.java
@@ -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();
+    }
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AgreementController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AgreementController.java
new file mode 100644
index 0000000..3ca67ec
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AgreementController.java
@@ -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();
+    }
+
+
+}
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AppUserController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AppUserController.java
new file mode 100644
index 0000000..a1f9d07
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/AppUserController.java
@@ -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();
+        }
+    }
+
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/BannerController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/BannerController.java
new file mode 100644
index 0000000..66e9ce2
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/BannerController.java
@@ -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));
+    }
+
+
+}
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityController.java
new file mode 100644
index 0000000..867ea30
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityController.java
@@ -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));
+    }
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityCourierController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityCourierController.java
new file mode 100644
index 0000000..b811b74
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CommunityCourierController.java
@@ -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;
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CourierController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CourierController.java
new file mode 100644
index 0000000..c744f95
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/CourierController.java
@@ -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();
+    }
+
+
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/EvaluationController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/EvaluationController.java
new file mode 100644
index 0000000..f4990dd
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/EvaluationController.java
@@ -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();
+    }
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/FeedbackController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/FeedbackController.java
new file mode 100644
index 0000000..ce01812
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/FeedbackController.java
@@ -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();
+    }
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/OrderController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/OrderController.java
new file mode 100644
index 0000000..9bc2d10
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/OrderController.java
@@ -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, "订单管理");
+    }
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/PhoneController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/PhoneController.java
new file mode 100644
index 0000000..44eacf8
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/PhoneController.java
@@ -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();
+    }
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/RegionController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/RegionController.java
new file mode 100644
index 0000000..7cb0c8e
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/RegionController.java
@@ -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);
+    }
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/ReportController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/ReportController.java
new file mode 100644
index 0000000..5f61c48
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/ReportController.java
@@ -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();
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/SystemConfigController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/SystemConfigController.java
new file mode 100644
index 0000000..0f723e3
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/SystemConfigController.java
@@ -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);
+    }
+
+}
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/UserCancellationLogController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/UserCancellationLogController.java
new file mode 100644
index 0000000..a20cae4
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/UserCancellationLogController.java
@@ -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;
+}    
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipOrderController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipOrderController.java
new file mode 100644
index 0000000..cd09c9b
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipOrderController.java
@@ -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();
+        }
+    }
+
+
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipSettingController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipSettingController.java
new file mode 100644
index 0000000..9c7c166
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/errand/VipSettingController.java
@@ -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());
+    }
+
+
+
+}
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
new file mode 100644
index 0000000..220e6f7
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
@@ -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();
+//    }
+//}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java
new file mode 100644
index 0000000..2af92b4
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java
@@ -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);
+//    }
+//}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
new file mode 100644
index 0000000..fe7cfc8
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
@@ -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();
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
new file mode 100644
index 0000000..bf88038
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
@@ -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();
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
new file mode 100644
index 0000000..02652da
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
@@ -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();
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
new file mode 100644
index 0000000..0274448
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
@@ -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();
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
new file mode 100644
index 0000000..a56d0ff
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
@@ -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));
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
new file mode 100644
index 0000000..1e1e337
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
@@ -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();
+    }
+
+
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
new file mode 100644
index 0000000..ba5fe36
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
@@ -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);
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java
new file mode 100644
index 0000000..ae98b5f
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java
@@ -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());
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
new file mode 100644
index 0000000..43148cb
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -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("退出成功");
+    }
+}
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java
new file mode 100644
index 0000000..e2d828c
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java
@@ -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));
+    }*/
+}
\ No newline at end of file
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
new file mode 100644
index 0000000..9c773d6
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
@@ -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));
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
new file mode 100644
index 0000000..1277100
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
@@ -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);
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
new file mode 100644
index 0000000..fe3f807
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
@@ -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("上传图片异常,请联系管理员");
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
new file mode 100644
index 0000000..598c24f
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
@@ -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);
+    }
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
new file mode 100644
index 0000000..26ff4ad
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
@@ -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;
+    }*/
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
new file mode 100644
index 0000000..6f99c0a
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -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));
+    }*/
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/controller/task/MessageTask.java b/pt-admin/src/main/java/com/ruoyi/web/controller/task/MessageTask.java
new file mode 100644
index 0000000..f0eec37
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/controller/task/MessageTask.java
@@ -0,0 +1,11 @@
+package com.ruoyi.web.controller.task;
+
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class MessageTask {
+
+
+
+}
diff --git a/pt-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/pt-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java
new file mode 100644
index 0000000..5627903
--- /dev/null
+++ b/pt-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java
@@ -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();
+    }
+}
diff --git a/pt-admin/src/main/resources/META-INF/spring-devtools.properties b/pt-admin/src/main/resources/META-INF/spring-devtools.properties
new file mode 100644
index 0000000..37e7b58
--- /dev/null
+++ b/pt-admin/src/main/resources/META-INF/spring-devtools.properties
@@ -0,0 +1 @@
+restart.include.json=/com.alibaba.fastjson2.*.jar
\ No newline at end of file
diff --git a/pt-admin/src/main/resources/application-druid.yml b/pt-admin/src/main/resources/application-druid.yml
new file mode 100644
index 0000000..f4266e0
--- /dev/null
+++ b/pt-admin/src/main/resources/application-druid.yml
@@ -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
\ No newline at end of file
diff --git a/pt-admin/src/main/resources/application.yml b/pt-admin/src/main/resources/application.yml
new file mode 100644
index 0000000..4d3c2a3
--- /dev/null
+++ b/pt-admin/src/main/resources/application.yml
@@ -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
\ No newline at end of file
diff --git a/pt-admin/src/main/resources/banner.txt b/pt-admin/src/main/resources/banner.txt
new file mode 100644
index 0000000..c0e09cd
--- /dev/null
+++ b/pt-admin/src/main/resources/banner.txt
@@ -0,0 +1,2 @@
+Application Version: ${ruoyi.version}
+Spring Boot Version: ${spring-boot.version}
diff --git a/pt-admin/src/main/resources/i18n/messages.properties b/pt-admin/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000..93de005
--- /dev/null
+++ b/pt-admin/src/main/resources/i18n/messages.properties
@@ -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}]
diff --git a/pt-admin/src/main/resources/logback.xml b/pt-admin/src/main/resources/logback.xml
new file mode 100644
index 0000000..a4bbc5d
--- /dev/null
+++ b/pt-admin/src/main/resources/logback.xml
@@ -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> 
\ No newline at end of file
diff --git a/pt-admin/src/main/resources/mybatis/mybatis-config.xml b/pt-admin/src/main/resources/mybatis/mybatis-config.xml
new file mode 100644
index 0000000..ac47c03
--- /dev/null
+++ b/pt-admin/src/main/resources/mybatis/mybatis-config.xml
@@ -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>
diff --git a/pt-admin/target/classes/META-INF/spring-devtools.properties b/pt-admin/target/classes/META-INF/spring-devtools.properties
new file mode 100644
index 0000000..37e7b58
--- /dev/null
+++ b/pt-admin/target/classes/META-INF/spring-devtools.properties
@@ -0,0 +1 @@
+restart.include.json=/com.alibaba.fastjson2.*.jar
\ No newline at end of file
diff --git a/pt-admin/target/classes/application-druid.yml b/pt-admin/target/classes/application-druid.yml
new file mode 100644
index 0000000..f4266e0
--- /dev/null
+++ b/pt-admin/target/classes/application-druid.yml
@@ -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
\ No newline at end of file
diff --git a/pt-admin/target/classes/application.yml b/pt-admin/target/classes/application.yml
new file mode 100644
index 0000000..4d3c2a3
--- /dev/null
+++ b/pt-admin/target/classes/application.yml
@@ -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
\ No newline at end of file
diff --git a/pt-admin/target/classes/banner.txt b/pt-admin/target/classes/banner.txt
new file mode 100644
index 0000000..c0e09cd
--- /dev/null
+++ b/pt-admin/target/classes/banner.txt
@@ -0,0 +1,2 @@
+Application Version: ${ruoyi.version}
+Spring Boot Version: ${spring-boot.version}
diff --git a/pt-admin/target/classes/i18n/messages.properties b/pt-admin/target/classes/i18n/messages.properties
new file mode 100644
index 0000000..93de005
--- /dev/null
+++ b/pt-admin/target/classes/i18n/messages.properties
@@ -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}]
diff --git a/pt-admin/target/classes/logback.xml b/pt-admin/target/classes/logback.xml
new file mode 100644
index 0000000..a4bbc5d
--- /dev/null
+++ b/pt-admin/target/classes/logback.xml
@@ -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> 
\ No newline at end of file
diff --git a/pt-admin/target/classes/mybatis/mybatis-config.xml b/pt-admin/target/classes/mybatis/mybatis-config.xml
new file mode 100644
index 0000000..ac47c03
--- /dev/null
+++ b/pt-admin/target/classes/mybatis/mybatis-config.xml
@@ -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>
diff --git a/pt-common/pom.xml b/pt-common/pom.xml
new file mode 100644
index 0000000..1bd1439
--- /dev/null
+++ b/pt-common/pom.xml
@@ -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>
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java b/pt-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java
new file mode 100644
index 0000000..1d6d4f4
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java
@@ -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
+{
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/pt-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
new file mode 100644
index 0000000..be49c80
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/DataScope.java
@@ -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 "";
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/pt-common/src/main/java/com/ruoyi/common/annotation/DataSource.java
new file mode 100644
index 0000000..79cd191
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/DataSource.java
@@ -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;
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/pt-common/src/main/java/com/ruoyi/common/annotation/Excel.java
new file mode 100644
index 0000000..765d8e3
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/Excel.java
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/Excels.java b/pt-common/src/main/java/com/ruoyi/common/annotation/Excels.java
new file mode 100644
index 0000000..1f1cc81
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/Excels.java
@@ -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();
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/Log.java b/pt-common/src/main/java/com/ruoyi/common/annotation/Log.java
new file mode 100644
index 0000000..1eb8e49
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/Log.java
@@ -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 {};
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java b/pt-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java
new file mode 100644
index 0000000..0f024c7
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java
@@ -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;
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java b/pt-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java
new file mode 100644
index 0000000..b769748
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java
@@ -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 "不允许重复提交,请稍候再试";
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java b/pt-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java
new file mode 100644
index 0000000..c0621e9
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java
@@ -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();
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/pt-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java
new file mode 100644
index 0000000..29281cf
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java
@@ -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";
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java b/pt-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java
new file mode 100644
index 0000000..e819a1d
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java
@@ -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;
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/pt-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
new file mode 100644
index 0000000..f7394fe
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
@@ -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:";
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/Constants.java b/pt-common/src/main/java/com/ruoyi/common/constant/Constants.java
new file mode 100644
index 0000000..0c384c6
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -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" };
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/pt-common/src/main/java/com/ruoyi/common/constant/GenConstants.java
new file mode 100644
index 0000000..7d899d4
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/GenConstants.java
@@ -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";
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/pt-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java
new file mode 100644
index 0000000..a983c77
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java
@@ -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;
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/pt-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java
new file mode 100644
index 0000000..62ad815
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java
@@ -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;
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/SecurityConstants.java b/pt-common/src/main/java/com/ruoyi/common/constant/SecurityConstants.java
new file mode 100644
index 0000000..6d13eb3
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/SecurityConstants.java
@@ -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";
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/TokenConstants.java b/pt-common/src/main/java/com/ruoyi/common/constant/TokenConstants.java
new file mode 100644
index 0000000..cbd82e0
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/TokenConstants.java
@@ -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";
+
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/pt-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
new file mode 100644
index 0000000..c5d1429
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
@@ -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;
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/pt-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
new file mode 100644
index 0000000..a685e06
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java
new file mode 100644
index 0000000..5b3a9e2
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
new file mode 100644
index 0000000..cda2ed1
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/BaseResult.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/BaseResult.java
new file mode 100644
index 0000000..1360d52
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/BaseResult.java
@@ -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);
+	}
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/R.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/R.java
new file mode 100644
index 0000000..ef15802
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/R.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/ResponseUtils.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/ResponseUtils.java
new file mode 100644
index 0000000..4fd0e1b
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/ResponseUtils.java
@@ -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;
+    }
+
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java
new file mode 100644
index 0000000..a180a18
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
new file mode 100644
index 0000000..ae25df2
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
new file mode 100644
index 0000000..fb18c5c
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
new file mode 100644
index 0000000..738f12c
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
new file mode 100644
index 0000000..e324fcf
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
new file mode 100644
index 0000000..ad73692
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
new file mode 100644
index 0000000..21699b9
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
new file mode 100644
index 0000000..791acf8
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
new file mode 100644
index 0000000..eaf033c
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
new file mode 100644
index 0000000..d90dbfd
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java b/pt-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java
new file mode 100644
index 0000000..868a1fc
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java
@@ -0,0 +1,11 @@
+package com.ruoyi.common.core.domain.model;
+
+/**
+ * 用户注册对象
+ * 
+ * @author ruoyi
+ */
+public class RegisterBody extends LoginBody
+{
+
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java b/pt-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java
new file mode 100644
index 0000000..78c75bb
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java b/pt-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
new file mode 100644
index 0000000..2fff93e
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java b/pt-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java
new file mode 100644
index 0000000..a120c30
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/pt-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
new file mode 100644
index 0000000..44e80d8
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/pt-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java
new file mode 100644
index 0000000..84124aa
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/pt-common/src/main/java/com/ruoyi/common/core/text/Convert.java
new file mode 100644
index 0000000..cbf118e
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/text/Convert.java
@@ -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("^整$", "零元整");
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/pt-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java
new file mode 100644
index 0000000..c78ac77
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/easyExcel/BigDecimalConverter.java b/pt-common/src/main/java/com/ruoyi/common/easyExcel/BigDecimalConverter.java
new file mode 100644
index 0000000..c34d958
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/easyExcel/BigDecimalConverter.java
@@ -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()));
+    }
+}
+
diff --git a/pt-common/src/main/java/com/ruoyi/common/easyExcel/DateConverter.java b/pt-common/src/main/java/com/ruoyi/common/easyExcel/DateConverter.java
new file mode 100644
index 0000000..ba20d34
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/easyExcel/DateConverter.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/easyExcel/MultiDropdownWriteHandler.java b/pt-common/src/main/java/com/ruoyi/common/easyExcel/MultiDropdownWriteHandler.java
new file mode 100644
index 0000000..a76da78
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/easyExcel/MultiDropdownWriteHandler.java
@@ -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);
+        }
+    }
+}
+
+
diff --git a/pt-common/src/main/java/com/ruoyi/common/easyExcel/NumberConverter.java b/pt-common/src/main/java/com/ruoyi/common/easyExcel/NumberConverter.java
new file mode 100644
index 0000000..70a1393
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/easyExcel/NumberConverter.java
@@ -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()));
+    }
+}
+
diff --git a/pt-common/src/main/java/com/ruoyi/common/easyExcel/StringConverter.java b/pt-common/src/main/java/com/ruoyi/common/easyExcel/StringConverter.java
new file mode 100644
index 0000000..77f51bb
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/easyExcel/StringConverter.java
@@ -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()));
+    }
+}
+
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java b/pt-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java
new file mode 100644
index 0000000..10b7306
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java
@@ -0,0 +1,20 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 操作状态
+ * 
+ * @author ruoyi
+ *
+ */
+public enum BusinessStatus
+{
+    /**
+     * 成功
+     */
+    SUCCESS,
+
+    /**
+     * 失败
+     */
+    FAIL,
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/BusinessType.java b/pt-common/src/main/java/com/ruoyi/common/enums/BusinessType.java
new file mode 100644
index 0000000..2e17c4a
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/BusinessType.java
@@ -0,0 +1,59 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 业务操作类型
+ * 
+ * @author ruoyi
+ */
+public enum BusinessType
+{
+    /**
+     * 其它
+     */
+    OTHER,
+
+    /**
+     * 新增
+     */
+    INSERT,
+
+    /**
+     * 修改
+     */
+    UPDATE,
+
+    /**
+     * 删除
+     */
+    DELETE,
+
+    /**
+     * 授权
+     */
+    GRANT,
+
+    /**
+     * 导出
+     */
+    EXPORT,
+
+    /**
+     * 导入
+     */
+    IMPORT,
+
+    /**
+     * 强退
+     */
+    FORCE,
+
+    /**
+     * 生成代码
+     */
+    GENCODE,
+    
+    /**
+     * 清空数据
+     */
+    CLEAN,
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/pt-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java
new file mode 100644
index 0000000..0d945be
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java
@@ -0,0 +1,19 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 数据源
+ * 
+ * @author ruoyi
+ */
+public enum DataSourceType
+{
+    /**
+     * 主库
+     */
+    MASTER,
+
+    /**
+     * 从库
+     */
+    SLAVE
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java b/pt-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
new file mode 100644
index 0000000..07f02ee
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/pt-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java
new file mode 100644
index 0000000..be6f739
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java
@@ -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));
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/LimitType.java b/pt-common/src/main/java/com/ruoyi/common/enums/LimitType.java
new file mode 100644
index 0000000..c609fd8
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/LimitType.java
@@ -0,0 +1,20 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 限流类型
+ *
+ * @author ruoyi
+ */
+
+public enum LimitType
+{
+    /**
+     * 默认策略全局限流
+     */
+    DEFAULT,
+
+    /**
+     * 根据请求者IP进行限流
+     */
+    IP
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/OperatorType.java b/pt-common/src/main/java/com/ruoyi/common/enums/OperatorType.java
new file mode 100644
index 0000000..bdd143c
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/OperatorType.java
@@ -0,0 +1,24 @@
+package com.ruoyi.common.enums;
+
+/**
+ * 操作人类别
+ * 
+ * @author ruoyi
+ */
+public enum OperatorType
+{
+    /**
+     * 其它
+     */
+    OTHER,
+
+    /**
+     * 后台用户
+     */
+    MANAGE,
+
+    /**
+     * 手机端用户
+     */
+    MOBILE
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/enums/UserStatus.java b/pt-common/src/main/java/com/ruoyi/common/enums/UserStatus.java
new file mode 100644
index 0000000..d7ff44a
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/enums/UserStatus.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/BaseException.java b/pt-common/src/main/java/com/ruoyi/common/exception/BaseException.java
new file mode 100644
index 0000000..8a92643
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/BaseException.java
@@ -0,0 +1,15 @@
+package com.ruoyi.common.exception;
+
+/**
+ * 业务异常
+ */
+public class BaseException extends RuntimeException {
+
+    public BaseException() {
+    }
+
+    public BaseException(String msg) {
+        super(msg);
+    }
+
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/pt-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java
new file mode 100644
index 0000000..f6ad2ab
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java
@@ -0,0 +1,15 @@
+package com.ruoyi.common.exception;
+
+/**
+ * 演示模式异常
+ * 
+ * @author ruoyi
+ */
+public class DemoModeException extends RuntimeException
+{
+    private static final long serialVersionUID = 1L;
+
+    public DemoModeException()
+    {
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/GlobalException.java b/pt-common/src/main/java/com/ruoyi/common/exception/GlobalException.java
new file mode 100644
index 0000000..c3a03d9
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/GlobalException.java
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/ServiceException.java b/pt-common/src/main/java/com/ruoyi/common/exception/ServiceException.java
new file mode 100644
index 0000000..fcc7ab6
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/ServiceException.java
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/UtilException.java b/pt-common/src/main/java/com/ruoyi/common/exception/UtilException.java
new file mode 100644
index 0000000..980fa46
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/UtilException.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java b/pt-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java
new file mode 100644
index 0000000..b55d72e
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/file/FileException.java b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileException.java
new file mode 100644
index 0000000..871f09b
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileException.java
@@ -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);
+    }
+
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java
new file mode 100644
index 0000000..70e0ec9
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java
@@ -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 });
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java
new file mode 100644
index 0000000..ec6ab05
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java
@@ -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 });
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java
new file mode 100644
index 0000000..f45e7ef
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/pt-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java
new file mode 100644
index 0000000..011f308
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java
@@ -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);
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/pt-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java
new file mode 100644
index 0000000..a567b40
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java
@@ -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
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java b/pt-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java
new file mode 100644
index 0000000..2bf5038
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java
new file mode 100644
index 0000000..389dbc7
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java
new file mode 100644
index 0000000..85f9486
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/user/UserException.java b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserException.java
new file mode 100644
index 0000000..c292d70
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserException.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java
new file mode 100644
index 0000000..eff8181
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java
new file mode 100644
index 0000000..a7f3e5f
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java
new file mode 100644
index 0000000..c887cf1
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java
@@ -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 });
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/pt-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java
new file mode 100644
index 0000000..e1e431b
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/pt-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
new file mode 100644
index 0000000..a1bcfe2
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
@@ -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()
+    {
+
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/pt-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
new file mode 100644
index 0000000..407d1ba
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
@@ -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)
+            {
+
+            }
+        };
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/pt-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
new file mode 100644
index 0000000..5c4cbe4
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
@@ -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()
+    {
+
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/pt-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
new file mode 100644
index 0000000..05149f0
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
@@ -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);
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/Arith.java b/pt-common/src/main/java/com/ruoyi/common/utils/Arith.java
new file mode 100644
index 0000000..9f95c0f
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/Arith.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
new file mode 100644
index 0000000..fb2ae21
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
@@ -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());
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java b/pt-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java
new file mode 100644
index 0000000..f8a4c02
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
new file mode 100644
index 0000000..f198462
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/pt-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java
new file mode 100644
index 0000000..214e4a0
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/LogUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/LogUtils.java
new file mode 100644
index 0000000..0de30c6
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/LogUtils.java
@@ -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() + "]";
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java
new file mode 100644
index 0000000..7dac75a
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java
@@ -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());
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/PageUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
new file mode 100644
index 0000000..70e9b08
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
new file mode 100644
index 0000000..7777e8b
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
@@ -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"));
+    }
+
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
new file mode 100644
index 0000000..febb603
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
@@ -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;
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
new file mode 100644
index 0000000..83f9fbf
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
@@ -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();
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/Threads.java b/pt-common/src/main/java/com/ruoyi/common/utils/Threads.java
new file mode 100644
index 0000000..71fe6d5
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/Threads.java
@@ -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);
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java
new file mode 100644
index 0000000..4463662
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java
@@ -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));
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java
new file mode 100644
index 0000000..80bfed7
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java
@@ -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);
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java
new file mode 100644
index 0000000..68130b9
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
new file mode 100644
index 0000000..d5455c4
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
new file mode 100644
index 0000000..821e475
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
@@ -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();
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java
new file mode 100644
index 0000000..432dfda
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java
@@ -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);
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
new file mode 100644
index 0000000..f968f1a
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
@@ -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 "";
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/pt-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java
new file mode 100644
index 0000000..f52e83e
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java
@@ -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));
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/pt-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java
new file mode 100644
index 0000000..ebff3fd
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java
@@ -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));
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/pt-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
new file mode 100644
index 0000000..589d123
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
@@ -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();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
new file mode 100644
index 0000000..534d21c
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
new file mode 100644
index 0000000..edfe419
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
new file mode 100644
index 0000000..8e89e30
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
@@ -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;
+    }
+}
\ No newline at end of file
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java b/pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java
new file mode 100644
index 0000000..ccab288
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java
@@ -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);
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
new file mode 100644
index 0000000..e63cf14
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
new file mode 100644
index 0000000..b19953e
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
@@ -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);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/pt-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java
new file mode 100644
index 0000000..ca1cd92
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java
@@ -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;
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/pt-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java
new file mode 100644
index 0000000..c1c58db
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java
@@ -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 --git a/pt-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java
new file mode 100644
index 0000000..4e3f603
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java
@@ -0,0 +1,164 @@
+package com.ruoyi.common.utils.spring;
+
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * spring工具类 方便在非spring管理环境中获取bean
+ * 
+ * @author ruoyi
+ */
+@Component
+public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware 
+{
+    /** Spring应用上下文环境 */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
+    {
+        SpringUtils.beanFactory = beanFactory;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
+    {
+        SpringUtils.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws org.springframework.beans.BeansException
+     *
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException
+    {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws org.springframework.beans.BeansException
+     *
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException
+    {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name)
+    {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     *
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     *
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     *
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
+    {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     * 
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker)
+    {
+        Object proxy = AopContext.currentProxy();
+        if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass())
+        {
+            return (T) proxy;
+        }
+        return invoker;
+    }
+
+    /**
+     * 获取当前的环境配置,无配置返回null
+     *
+     * @return 当前的环境配置
+     */
+    public static String[] getActiveProfiles()
+    {
+        return applicationContext.getEnvironment().getActiveProfiles();
+    }
+
+    /**
+     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
+     *
+     * @return 当前的环境配置
+     */
+    public static String getActiveProfile()
+    {
+        final String[] activeProfiles = getActiveProfiles();
+        return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
+    }
+
+    /**
+     * 获取配置文件中的值
+     *
+     * @param key 配置文件的key
+     * @return 当前的配置文件的值
+     *
+     */
+    public static String getRequiredProperty(String key)
+    {
+        return applicationContext.getEnvironment().getRequiredProperty(key);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/pt-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java
new file mode 100644
index 0000000..48720dc
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java
@@ -0,0 +1,70 @@
+package com.ruoyi.common.utils.sql;
+
+import com.ruoyi.common.exception.UtilException;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * sql操作工具类
+ * 
+ * @author ruoyi
+ */
+public class SqlUtil
+{
+    /**
+     * 定义常用的 sql关键字
+     */
+    public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
+
+    /**
+     * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
+     */
+    public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
+
+    /**
+     * 限制orderBy最大长度
+     */
+    private static final int ORDER_BY_MAX_LENGTH = 500;
+
+    /**
+     * 检查字符,防止注入绕过
+     */
+    public static String escapeOrderBySql(String value)
+    {
+        if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
+        {
+            throw new UtilException("参数不符合规范,不能进行查询");
+        }
+        if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH)
+        {
+            throw new UtilException("参数已超过最大限制,不能进行查询");
+        }
+        return value;
+    }
+
+    /**
+     * 验证 order by 语法是否符合规范
+     */
+    public static boolean isValidOrderBySql(String value)
+    {
+        return value.matches(SQL_PATTERN);
+    }
+
+    /**
+     * SQL关键字检查
+     */
+    public static void filterKeyword(String value)
+    {
+        if (StringUtils.isEmpty(value))
+        {
+            return;
+        }
+        String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
+        for (String sqlKeyword : sqlKeywords)
+        {
+            if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
+            {
+                throw new UtilException("参数存在SQL注入风险");
+            }
+        }
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java b/pt-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java
new file mode 100644
index 0000000..2c84427
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java
@@ -0,0 +1,49 @@
+package com.ruoyi.common.utils.uuid;
+
+/**
+ * ID生成器工具类
+ * 
+ * @author ruoyi
+ */
+public class IdUtils
+{
+    /**
+     * 获取随机UUID
+     * 
+     * @return 随机UUID
+     */
+    public static String randomUUID()
+    {
+        return UUID.randomUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线
+     * 
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String simpleUUID()
+    {
+        return UUID.randomUUID().toString(true);
+    }
+
+    /**
+     * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
+     * 
+     * @return 随机UUID
+     */
+    public static String fastUUID()
+    {
+        return UUID.fastUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
+     * 
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String fastSimpleUUID()
+    {
+        return UUID.fastUUID().toString(true);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java b/pt-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java
new file mode 100644
index 0000000..bf99611
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java
@@ -0,0 +1,86 @@
+package com.ruoyi.common.utils.uuid;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * @author ruoyi 序列生成类
+ */
+public class Seq
+{
+    // 通用序列类型
+    public static final String commSeqType = "COMMON";
+
+    // 上传序列类型
+    public static final String uploadSeqType = "UPLOAD";
+
+    // 通用接口序列数
+    private static AtomicInteger commSeq = new AtomicInteger(1);
+
+    // 上传接口序列数
+    private static AtomicInteger uploadSeq = new AtomicInteger(1);
+
+    // 机器标识
+    private static final String machineCode = "A";
+
+    /**
+     * 获取通用序列号
+     * 
+     * @return 序列值
+     */
+    public static String getId()
+    {
+        return getId(commSeqType);
+    }
+    
+    /**
+     * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
+     * 
+     * @return 序列值
+     */
+    public static String getId(String type)
+    {
+        AtomicInteger atomicInt = commSeq;
+        if (uploadSeqType.equals(type))
+        {
+            atomicInt = uploadSeq;
+        }
+        return getId(atomicInt, 3);
+    }
+
+    /**
+     * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
+     * 
+     * @param atomicInt 序列数
+     * @param length 数值长度
+     * @return 序列值
+     */
+    public static String getId(AtomicInteger atomicInt, int length)
+    {
+        String result = DateUtils.dateTimeNow();
+        result += machineCode;
+        result += getSeq(atomicInt, length);
+        return result;
+    }
+
+    /**
+     * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
+     * 
+     * @return 序列值
+     */
+    private synchronized static String getSeq(AtomicInteger atomicInt, int length)
+    {
+        // 先取值再+1
+        int value = atomicInt.getAndIncrement();
+
+        // 如果更新后值>=10 的 (length)幂次方则重置为1
+        int maxSeq = (int) Math.pow(10, length);
+        if (atomicInt.get() >= maxSeq)
+        {
+            atomicInt.set(1);
+        }
+        // 转字符串,用0左补齐
+        return StringUtils.padl(value, length);
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java b/pt-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java
new file mode 100644
index 0000000..a5585d6
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java
@@ -0,0 +1,484 @@
+package com.ruoyi.common.utils.uuid;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import com.ruoyi.common.exception.UtilException;
+
+/**
+ * 提供通用唯一识别码(universally unique identifier)(UUID)实现
+ *
+ * @author ruoyi
+ */
+public final class UUID implements java.io.Serializable, Comparable<UUID>
+{
+    private static final long serialVersionUID = -1185015143654744140L;
+
+    /**
+     * SecureRandom 的单例
+     *
+     */
+    private static class Holder
+    {
+        static final SecureRandom numberGenerator = getSecureRandom();
+    }
+
+    /** 此UUID的最高64有效位 */
+    private final long mostSigBits;
+
+    /** 此UUID的最低64有效位 */
+    private final long leastSigBits;
+
+    /**
+     * 私有构造
+     * 
+     * @param data 数据
+     */
+    private UUID(byte[] data)
+    {
+        long msb = 0;
+        long lsb = 0;
+        assert data.length == 16 : "data must be 16 bytes in length";
+        for (int i = 0; i < 8; i++)
+        {
+            msb = (msb << 8) | (data[i] & 0xff);
+        }
+        for (int i = 8; i < 16; i++)
+        {
+            lsb = (lsb << 8) | (data[i] & 0xff);
+        }
+        this.mostSigBits = msb;
+        this.leastSigBits = lsb;
+    }
+
+    /**
+     * 使用指定的数据构造新的 UUID。
+     *
+     * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
+     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
+     */
+    public UUID(long mostSigBits, long leastSigBits)
+    {
+        this.mostSigBits = mostSigBits;
+        this.leastSigBits = leastSigBits;
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。
+     * 
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID fastUUID()
+    {
+        return randomUUID(false);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     * 
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID()
+    {
+        return randomUUID(true);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     * 
+     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID(boolean isSecure)
+    {
+        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
+
+        byte[] randomBytes = new byte[16];
+        ng.nextBytes(randomBytes);
+        randomBytes[6] &= 0x0f; /* clear version */
+        randomBytes[6] |= 0x40; /* set to version 4 */
+        randomBytes[8] &= 0x3f; /* clear variant */
+        randomBytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(randomBytes);
+    }
+
+    /**
+     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
+     *
+     * @param name 用于构造 UUID 的字节数组。
+     *
+     * @return 根据指定数组生成的 {@code UUID}
+     */
+    public static UUID nameUUIDFromBytes(byte[] name)
+    {
+        MessageDigest md;
+        try
+        {
+            md = MessageDigest.getInstance("MD5");
+        }
+        catch (NoSuchAlgorithmException nsae)
+        {
+            throw new InternalError("MD5 not supported");
+        }
+        byte[] md5Bytes = md.digest(name);
+        md5Bytes[6] &= 0x0f; /* clear version */
+        md5Bytes[6] |= 0x30; /* set to version 3 */
+        md5Bytes[8] &= 0x3f; /* clear variant */
+        md5Bytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(md5Bytes);
+    }
+
+    /**
+     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
+     *
+     * @param name 指定 {@code UUID} 字符串
+     * @return 具有指定值的 {@code UUID}
+     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
+     *
+     */
+    public static UUID fromString(String name)
+    {
+        String[] components = name.split("-");
+        if (components.length != 5)
+        {
+            throw new IllegalArgumentException("Invalid UUID string: " + name);
+        }
+        for (int i = 0; i < 5; i++)
+        {
+            components[i] = "0x" + components[i];
+        }
+
+        long mostSigBits = Long.decode(components[0]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[1]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[2]).longValue();
+
+        long leastSigBits = Long.decode(components[3]).longValue();
+        leastSigBits <<= 48;
+        leastSigBits |= Long.decode(components[4]).longValue();
+
+        return new UUID(mostSigBits, leastSigBits);
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最低有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中的最低有效 64 位。
+     */
+    public long getLeastSignificantBits()
+    {
+        return leastSigBits;
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最高有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中最高有效 64 位。
+     */
+    public long getMostSignificantBits()
+    {
+        return mostSigBits;
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
+     * <p>
+     * 版本号具有以下含意:
+     * <ul>
+     * <li>1 基于时间的 UUID
+     * <li>2 DCE 安全 UUID
+     * <li>3 基于名称的 UUID
+     * <li>4 随机生成的 UUID
+     * </ul>
+     *
+     * @return 此 {@code UUID} 的版本号
+     */
+    public int version()
+    {
+        // Version is bits masked by 0x000000000000F000 in MS long
+        return (int) ((mostSigBits >> 12) & 0x0f);
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
+     * <p>
+     * 变体号具有以下含意:
+     * <ul>
+     * <li>0 为 NCS 向后兼容保留
+     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
+     * <li>6 保留,微软向后兼容
+     * <li>7 保留供以后定义使用
+     * </ul>
+     *
+     * @return 此 {@code UUID} 相关联的变体号
+     */
+    public int variant()
+    {
+        // This field is composed of a varying number of bits.
+        // 0 - - Reserved for NCS backward compatibility
+        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
+        // 1 1 0 Reserved, Microsoft backward compatibility
+        // 1 1 1 Reserved for future definition.
+        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
+    }
+
+    /**
+     * 与此 UUID 相关联的时间戳值。
+     *
+     * <p>
+     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
+     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
+     *
+     * <p>
+     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
+     */
+    public long timestamp() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (mostSigBits & 0x0FFFL) << 48//
+                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
+                | mostSigBits >>> 32;
+    }
+
+    /**
+     * 与此 UUID 相关联的时钟序列值。
+     *
+     * <p>
+     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
+     * <p>
+     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
+     * UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的时钟序列
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public int clockSequence() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
+    }
+
+    /**
+     * 与此 UUID 相关的节点值。
+     *
+     * <p>
+     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
+     * <p>
+     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的节点值
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public long node() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return leastSigBits & 0x0000FFFFFFFFFFFFL;
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     * 
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     * 
+     * </blockquote>
+     *
+     * @return 此{@code UUID} 的字符串表现形式
+     * @see #toString(boolean)
+     */
+    @Override
+    public String toString()
+    {
+        return toString(false);
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     * 
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     * 
+     * </blockquote>
+     *
+     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
+     * @return 此{@code UUID} 的字符串表现形式
+     */
+    public String toString(boolean isSimple)
+    {
+        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
+        // time_low
+        builder.append(digits(mostSigBits >> 32, 8));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_mid
+        builder.append(digits(mostSigBits >> 16, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_high_and_version
+        builder.append(digits(mostSigBits, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // variant_and_sequence
+        builder.append(digits(leastSigBits >> 48, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // node
+        builder.append(digits(leastSigBits, 12));
+
+        return builder.toString();
+    }
+
+    /**
+     * 返回此 UUID 的哈希码。
+     *
+     * @return UUID 的哈希码值。
+     */
+    @Override
+    public int hashCode()
+    {
+        long hilo = mostSigBits ^ leastSigBits;
+        return ((int) (hilo >> 32)) ^ (int) hilo;
+    }
+
+    /**
+     * 将此对象与指定对象比较。
+     * <p>
+     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
+     *
+     * @param obj 要与之比较的对象
+     *
+     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
+     */
+    @Override
+    public boolean equals(Object obj)
+    {
+        if ((null == obj) || (obj.getClass() != UUID.class))
+        {
+            return false;
+        }
+        UUID id = (UUID) obj;
+        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
+    }
+
+    // Comparison Operations
+
+    /**
+     * 将此 UUID 与指定的 UUID 比较。
+     *
+     * <p>
+     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
+     *
+     * @param val 与此 UUID 比较的 UUID
+     *
+     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
+     *
+     */
+    @Override
+    public int compareTo(UUID val)
+    {
+        // The ordering is intentionally set up so that the UUIDs
+        // can simply be numerically compared as two numbers
+        return (this.mostSigBits < val.mostSigBits ? -1 : //
+                (this.mostSigBits > val.mostSigBits ? 1 : //
+                        (this.leastSigBits < val.leastSigBits ? -1 : //
+                                (this.leastSigBits > val.leastSigBits ? 1 : //
+                                        0))));
+    }
+
+    // -------------------------------------------------------------------------------------------------------------------
+    // Private method start
+    /**
+     * 返回指定数字对应的hex值
+     * 
+     * @param val 值
+     * @param digits 位
+     * @return 值
+     */
+    private static String digits(long val, int digits)
+    {
+        long hi = 1L << (digits * 4);
+        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
+    }
+
+    /**
+     * 检查是否为time-based版本UUID
+     */
+    private void checkTimeBase()
+    {
+        if (version() != 1)
+        {
+            throw new UnsupportedOperationException("Not a time-based UUID");
+        }
+    }
+
+    /**
+     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
+     * 
+     * @return {@link SecureRandom}
+     */
+    public static SecureRandom getSecureRandom()
+    {
+        try
+        {
+            return SecureRandom.getInstance("SHA1PRNG");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new UtilException(e);
+        }
+    }
+
+    /**
+     * 获取随机数生成器对象<br>
+     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
+     * 
+     * @return {@link ThreadLocalRandom}
+     */
+    public static ThreadLocalRandom getRandom()
+    {
+        return ThreadLocalRandom.current();
+    }
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/xss/Xss.java b/pt-common/src/main/java/com/ruoyi/common/xss/Xss.java
new file mode 100644
index 0000000..7bfdf04
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/xss/Xss.java
@@ -0,0 +1,27 @@
+package com.ruoyi.common.xss;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义xss校验注解
+ * 
+ * @author ruoyi
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
+@Constraint(validatedBy = { XssValidator.class })
+public @interface Xss
+{
+    String message()
+
+    default "不允许任何脚本运行";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}
diff --git a/pt-common/src/main/java/com/ruoyi/common/xss/XssValidator.java b/pt-common/src/main/java/com/ruoyi/common/xss/XssValidator.java
new file mode 100644
index 0000000..42f425c
--- /dev/null
+++ b/pt-common/src/main/java/com/ruoyi/common/xss/XssValidator.java
@@ -0,0 +1,39 @@
+package com.ruoyi.common.xss;
+
+import com.ruoyi.common.utils.StringUtils;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 自定义xss校验注解实现
+ * 
+ * @author ruoyi
+ */
+public class XssValidator implements ConstraintValidator<Xss, String>
+{
+    private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext)
+    {
+        if (StringUtils.isBlank(value))
+        {
+            return true;
+        }
+        return !containsHtml(value);
+    }
+
+    public static boolean containsHtml(String value)
+    {
+        StringBuilder sHtml = new StringBuilder();
+        Pattern pattern = Pattern.compile(HTML_PATTERN);
+        Matcher matcher = pattern.matcher(value);
+        while (matcher.find())
+        {
+            sHtml.append(matcher.group());
+        }
+        return pattern.matcher(sHtml).matches();
+    }
+}
\ No newline at end of file
diff --git a/pt-common/target/maven-archiver/pom.properties b/pt-common/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..624575f
--- /dev/null
+++ b/pt-common/target/maven-archiver/pom.properties
@@ -0,0 +1,3 @@
+artifactId=pt-common
+groupId=com.pt
+version=3.8.9
diff --git a/pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..f9a6489
--- /dev/null
+++ b/pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,129 @@
+com\ruoyi\common\filter\XssHttpServletRequestWrapper.class
+com\ruoyi\common\utils\ip\IpUtils.class
+com\ruoyi\common\utils\uuid\IdUtils.class
+com\ruoyi\common\annotation\RepeatSubmit.class
+com\ruoyi\common\constant\GenConstants.class
+com\ruoyi\common\core\domain\TreeEntity.class
+com\ruoyi\common\exception\base\BaseException.class
+com\ruoyi\common\utils\ip\AddressUtils.class
+com\ruoyi\common\constant\HttpStatus.class
+com\ruoyi\common\utils\DesensitizedUtil.class
+com\ruoyi\common\utils\sign\Base64.class
+com\ruoyi\common\xss\Xss.class
+com\ruoyi\common\exception\user\UserNotExistsException.class
+com\ruoyi\common\core\domain\model\LoginBody.class
+com\ruoyi\common\exception\user\UserPasswordNotMatchException.class
+com\ruoyi\common\annotation\Log.class
+com\ruoyi\common\core\controller\BaseController$1.class
+com\ruoyi\common\utils\file\ImageUtils.class
+com\ruoyi\common\config\RuoYiConfig.class
+com\ruoyi\common\enums\BusinessStatus.class
+com\ruoyi\common\utils\sign\Md5Utils.class
+com\ruoyi\common\utils\http\HttpUtils$TrustAnyHostnameVerifier.class
+com\ruoyi\common\exception\file\FileSizeLimitExceededException.class
+com\ruoyi\common\utils\Arith.class
+com\ruoyi\common\annotation\Excel$ColumnType.class
+com\ruoyi\common\core\domain\BaseResult.class
+com\ruoyi\common\annotation\DataScope.class
+com\ruoyi\common\easyExcel\NumberConverter.class
+com\ruoyi\common\core\domain\entity\SysDictType.class
+com\ruoyi\common\core\text\StrFormatter.class
+com\ruoyi\common\annotation\Excels.class
+com\ruoyi\common\config\serializer\SensitiveJsonSerializer.class
+com\ruoyi\common\core\domain\entity\SysDictData.class
+com\ruoyi\common\utils\http\HttpUtils$TrustAnyTrustManager.class
+com\ruoyi\common\enums\UserStatus.class
+com\ruoyi\common\exception\user\CaptchaExpireException.class
+com\ruoyi\common\annotation\Excel$Type.class
+com\ruoyi\common\exception\job\TaskException$Code.class
+com\ruoyi\common\utils\spring\SpringUtils.class
+com\ruoyi\common\utils\file\FileUploadUtils.class
+com\ruoyi\common\filter\RepeatedlyRequestWrapper$1.class
+com\ruoyi\common\utils\bean\BeanUtils.class
+com\ruoyi\common\utils\sql\SqlUtil.class
+com\ruoyi\common\easyExcel\BigDecimalConverter.class
+com\ruoyi\common\utils\file\MimeTypeUtils.class
+com\ruoyi\common\core\domain\ResponseUtils.class
+com\ruoyi\common\enums\BusinessType.class
+com\ruoyi\common\filter\PropertyPreExcludeFilter.class
+com\ruoyi\common\core\domain\model\LoginUser.class
+com\ruoyi\common\enums\DesensitizedType.class
+com\ruoyi\common\exception\file\InvalidExtensionException$InvalidMediaExtensionException.class
+com\ruoyi\common\exception\file\FileUploadException.class
+com\ruoyi\common\utils\poi\ExcelUtil.class
+com\ruoyi\common\exception\user\UserException.class
+com\ruoyi\common\utils\MessageUtils.class
+com\ruoyi\common\enums\DataSourceType.class
+com\ruoyi\common\exception\file\InvalidExtensionException$InvalidVideoExtensionException.class
+com\ruoyi\common\easyExcel\MultiDropdownWriteHandler.class
+com\ruoyi\common\enums\HttpMethod.class
+com\ruoyi\common\utils\PageUtils.class
+com\ruoyi\common\utils\LogUtils.class
+com\ruoyi\common\annotation\DataSource.class
+com\ruoyi\common\core\page\PageDomain.class
+com\ruoyi\common\filter\XssHttpServletRequestWrapper$1.class
+com\ruoyi\common\utils\ExceptionUtil.class
+com\ruoyi\common\utils\http\HttpHelper.class
+com\ruoyi\common\enums\LimitType.class
+com\ruoyi\common\annotation\Sensitive.class
+com\ruoyi\common\filter\RepeatedlyRequestWrapper.class
+com\ruoyi\common\exception\file\InvalidExtensionException$InvalidImageExtensionException.class
+com\ruoyi\common\core\controller\BaseController.class
+com\ruoyi\common\utils\StringUtils.class
+com\ruoyi\common\utils\poi\ExcelHandlerAdapter.class
+com\ruoyi\common\exception\DemoModeException.class
+com\ruoyi\common\enums\OperatorType.class
+com\ruoyi\common\exception\UtilException.class
+com\ruoyi\common\xss\XssValidator.class
+com\ruoyi\common\utils\DateUtils.class
+com\ruoyi\common\core\domain\entity\SysMenu.class
+com\ruoyi\common\core\page\TableDataInfo.class
+com\ruoyi\common\exception\GlobalException.class
+com\ruoyi\common\exception\job\TaskException.class
+com\ruoyi\common\constant\UserConstants.class
+com\ruoyi\common\core\text\Convert.class
+com\ruoyi\common\core\domain\entity\SysUser.class
+com\ruoyi\common\utils\file\FileUtils.class
+com\ruoyi\common\exception\user\BlackListException.class
+com\ruoyi\common\core\domain\TreeSelect.class
+com\ruoyi\common\filter\RepeatableFilter.class
+com\ruoyi\common\constant\Constants.class
+com\ruoyi\common\core\domain\entity\SysRole.class
+com\ruoyi\common\annotation\Anonymous.class
+com\ruoyi\common\exception\user\CaptchaException.class
+com\ruoyi\common\easyExcel\StringConverter.class
+com\ruoyi\common\core\domain\R.class
+com\ruoyi\common\utils\bean\BeanValidators.class
+com\ruoyi\common\core\domain\model\RegisterBody.class
+com\ruoyi\common\exception\file\FileException.class
+com\ruoyi\common\core\domain\BaseEntity.class
+com\ruoyi\common\utils\SecurityUtils.class
+com\ruoyi\common\core\redis\RedisCache.class
+com\ruoyi\common\annotation\Excel.class
+com\ruoyi\common\utils\uuid\UUID$Holder.class
+com\ruoyi\common\annotation\RateLimiter.class
+com\ruoyi\common\core\domain\entity\SysDept.class
+com\ruoyi\common\core\page\TableSupport.class
+com\ruoyi\common\utils\uuid\UUID.class
+com\ruoyi\common\utils\http\HttpUtils.class
+com\ruoyi\common\exception\file\InvalidExtensionException.class
+com\ruoyi\common\constant\ScheduleConstants.class
+com\ruoyi\common\exception\ServiceException.class
+com\ruoyi\common\utils\file\FileTypeUtils.class
+com\ruoyi\common\constant\ScheduleConstants$Status.class
+com\ruoyi\common\utils\html\EscapeUtil.class
+com\ruoyi\common\exception\file\InvalidExtensionException$InvalidFlashExtensionException.class
+com\ruoyi\common\utils\http\HttpUtils$1.class
+com\ruoyi\common\utils\ServletUtils.class
+com\ruoyi\common\utils\html\HTMLFilter.class
+com\ruoyi\common\utils\Threads.class
+com\ruoyi\common\utils\uuid\Seq.class
+com\ruoyi\common\filter\XssFilter.class
+com\ruoyi\common\core\domain\AjaxResult.class
+com\ruoyi\common\exception\file\FileNameLengthLimitExceededException.class
+com\ruoyi\common\core\text\CharsetKit.class
+com\ruoyi\common\easyExcel\DateConverter.class
+com\ruoyi\common\utils\reflect\ReflectUtils.class
+com\ruoyi\common\utils\DictUtils.class
+com\ruoyi\common\constant\CacheConstants.class
+com\ruoyi\common\exception\user\UserPasswordRetryLimitExceedException.class
diff --git a/pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..0d8f4b5
--- /dev/null
+++ b/pt-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,114 @@
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\Arith.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\config\RuoYiConfig.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\user\UserPasswordNotMatchException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\constant\UserConstants.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\UtilException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\DemoModeException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\user\UserPasswordRetryLimitExceedException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\text\Convert.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\base\BaseException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\poi\ExcelHandlerAdapter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\easyExcel\MultiDropdownWriteHandler.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\filter\RepeatedlyRequestWrapper.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\file\InvalidExtensionException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\BaseResult.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\http\HttpHelper.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\xss\Xss.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\constant\CacheConstants.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\DictUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\model\LoginUser.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\constant\ScheduleConstants.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\easyExcel\DateConverter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\DataSource.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\config\serializer\SensitiveJsonSerializer.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\text\StrFormatter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\model\LoginBody.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\DesensitizedType.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\file\FileSizeLimitExceededException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\LogUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\ip\AddressUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\RepeatSubmit.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\R.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\ExceptionUtil.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\file\MimeTypeUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\poi\ExcelUtil.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\TreeEntity.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\constant\GenConstants.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\Sensitive.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\ip\IpUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\html\EscapeUtil.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\Log.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\html\HTMLFilter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\reflect\ReflectUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\page\PageDomain.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\StringUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\xss\XssValidator.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\file\FileUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\Anonymous.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\job\TaskException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\RateLimiter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\spring\SpringUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\TreeSelect.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\controller\BaseController.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\Excel.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\BaseEntity.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\entity\SysMenu.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\constant\Constants.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\uuid\Seq.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\easyExcel\StringConverter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\user\CaptchaExpireException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\AjaxResult.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\OperatorType.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\page\TableDataInfo.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\HttpMethod.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\redis\RedisCache.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\file\FileException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\Threads.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\BusinessStatus.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\page\TableSupport.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\BusinessType.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\file\FileTypeUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\user\UserException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\file\FileUploadUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\user\BlackListException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\bean\BeanValidators.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\sign\Md5Utils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\DesensitizedUtil.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\text\CharsetKit.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\filter\XssFilter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\DataSourceType.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\MessageUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\SecurityUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\file\ImageUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\http\HttpUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\DataScope.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\entity\SysUser.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\ResponseUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\filter\PropertyPreExcludeFilter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\sql\SqlUtil.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\annotation\Excels.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\entity\SysDictData.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\constant\HttpStatus.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\sign\Base64.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\entity\SysRole.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\LimitType.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\GlobalException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\ServiceException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\ServletUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\uuid\IdUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\filter\RepeatableFilter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\entity\SysDictType.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\model\RegisterBody.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\filter\XssHttpServletRequestWrapper.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\bean\BeanUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\easyExcel\BigDecimalConverter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\core\domain\entity\SysDept.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\uuid\UUID.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\easyExcel\NumberConverter.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\file\FileUploadException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\file\FileNameLengthLimitExceededException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\user\CaptchaException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\exception\user\UserNotExistsException.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\PageUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\utils\DateUtils.java
+E:\hlg资料\项目\paotui\pt-common\src\main\java\com\ruoyi\common\enums\UserStatus.java
diff --git a/pt-errand/pom.xml b/pt-errand/pom.xml
new file mode 100644
index 0000000..a2b665e
--- /dev/null
+++ b/pt-errand/pom.xml
@@ -0,0 +1,48 @@
+<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/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>kunming-daiban</artifactId>
+        <groupId>com.pt</groupId>
+        <version>3.8.9</version>
+    </parent>
+
+    <groupId>com.pt</groupId>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>pt-errand</artifactId>
+
+    <description>
+        业务模块
+    </description>
+
+    <dependencies>
+        <!--短信验证http-->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <!-- Spring Boot WebSocket -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>1.69</version>
+        </dependency>
+
+        <!-- 通用工具-->
+        <dependency>
+            <groupId>com.pt</groupId>
+            <artifactId>pt-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.pt</groupId>
+            <artifactId>pt-system</artifactId>
+        </dependency>
+
+    </dependencies>
+</project>
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/AppUserStatusConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/AppUserStatusConstant.java
new file mode 100644
index 0000000..f76f3ea
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/AppUserStatusConstant.java
@@ -0,0 +1,13 @@
+package com.ruoyi.errand.constant;
+
+public class AppUserStatusConstant {
+
+    //正常
+    public static final Integer NORMAL = 1;
+
+    //冻结
+    public static final Integer FREEZE = 2;
+
+    //注销
+    public static final Integer LOGOUT = 3;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/DelFlagConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/DelFlagConstant.java
new file mode 100644
index 0000000..738795c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/DelFlagConstant.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.constant;
+
+public class DelFlagConstant {
+    //删除
+    public static final Integer DELETE = 1;
+
+    //未删除
+    public static final Integer UNDELETE = 0;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/IsDefaultConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/IsDefaultConstant.java
new file mode 100644
index 0000000..cbcfac0
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/IsDefaultConstant.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.constant;
+
+public class IsDefaultConstant {
+    //是
+    public static final Integer TRUE = 1;
+
+    //否
+    public static final Integer FALSE = 0;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstLoginConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstLoginConstant.java
new file mode 100644
index 0000000..2c129ec
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstLoginConstant.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.constant;
+
+public class IsFirstLoginConstant {
+    //是
+    public static final Integer YES = 1;
+
+    //否
+    public static final Integer NO = 0;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstOrder.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstOrder.java
new file mode 100644
index 0000000..054e0fa
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/IsFirstOrder.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.constant;
+
+public class IsFirstOrder {
+    //是
+    public static final Integer YES = 1;
+
+    //否
+    public static final Integer NO = 0;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/JwtClaimsConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/JwtClaimsConstant.java
new file mode 100644
index 0000000..2f238ef
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/JwtClaimsConstant.java
@@ -0,0 +1,7 @@
+package com.ruoyi.errand.constant;
+
+public class JwtClaimsConstant {
+    public static final String USER_PHONE = "phone";
+    public static final String USER_ID = "id";
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/MessageConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/MessageConstant.java
new file mode 100644
index 0000000..7fddc3f
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/MessageConstant.java
@@ -0,0 +1,20 @@
+package com.ruoyi.errand.constant;
+
+public class MessageConstant {
+    public static final String PASSWORD_ERROR = "密码错误";
+    public static final String PHONE_NOT_FOUND = "手机号不存在";
+
+    public static final String ACCOUNT_LOCKED = "账号被锁定";
+
+    public static final String PHONE_REPEAT="手机号重复";
+    public static final String VERIFICATION_CODE_ERROR="验证码错误";
+    public static final String PASSWORD_INCONSISTENCY="两次密码不一致";
+
+
+    public static final String ALREADY_EXISTS="已存在";
+    public static final String UNKNOWN_ERROR = "未知错误";
+
+    public static final String QUESTION_ID_NOT_FOUNT="该问题未找到答案";
+    public static final String USER_NOT_LOGIN = "用户未登录";
+    public static final String TOKEN_EXPIRED = "token过期";
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/StatusConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/StatusConstant.java
new file mode 100644
index 0000000..ecb428a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/StatusConstant.java
@@ -0,0 +1,15 @@
+package com.ruoyi.errand.constant;
+
+public class StatusConstant {
+    //启用
+    public static final Integer ENABLE = 0;
+
+    //禁用
+    public static final Integer DISABLE = 1;
+
+    //未处理
+    public static final Integer UN_DISPOSE = 0;
+
+    //已处理
+    public static final Integer DISPOSE = 1;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/constant/SystemConfigTypeConstant.java b/pt-errand/src/main/java/com/ruoyi/errand/constant/SystemConfigTypeConstant.java
new file mode 100644
index 0000000..1a8004b
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/constant/SystemConfigTypeConstant.java
@@ -0,0 +1,8 @@
+package com.ruoyi.errand.constant;
+
+public class SystemConfigTypeConstant {
+    //启动页管理
+    public static final Integer START_PAGE = 1;
+    //首页配置
+    public static final Integer HOME_PAGE = 2;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/context/BaseContext.java b/pt-errand/src/main/java/com/ruoyi/errand/context/BaseContext.java
new file mode 100644
index 0000000..12eb15a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/context/BaseContext.java
@@ -0,0 +1,20 @@
+package com.ruoyi.errand.context;
+
+import com.ruoyi.errand.domain.AppUser;
+
+public class BaseContext {
+
+    public static ThreadLocal<AppUser> threadLocal = new ThreadLocal<>();
+
+    public static void setCurrentUser(AppUser user) {
+        threadLocal.set(user);
+    }
+    public static AppUser getCurrentUser() {
+        return threadLocal.get();
+    }
+
+    public static void removeCurrentPhone() {
+        threadLocal.remove();
+    }
+
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/AddressBook.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/AddressBook.java
new file mode 100644
index 0000000..259b94b
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/AddressBook.java
@@ -0,0 +1,59 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_address_book")
+public class AddressBook implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("地址ID")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("用户ID")
+    @TableField("app_user_id")
+    private Long app_user_id;
+
+    @ApiModelProperty("收件人姓名")
+    @TableField("recipient_name")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    @TableField("recipient_phone")
+    private String recipientPhone;
+
+    @ApiModelProperty("关联小区ID")
+    @TableField("community_id")
+    private Integer communityId;
+
+    @ApiModelProperty("详细地址")
+    @TableField("address_detail")
+    private String addressDetail;
+
+    @ApiModelProperty("是否默认地址:0-否,1-是")
+    @TableField("is_default")
+    private Integer is_default;
+
+    @ApiModelProperty("添加时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime create_time;
+
+    @ApiModelProperty("更新时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime update_time;
+
+    @ApiModelProperty("是否删除:0-否,1-是")
+    @TableField("del_flag")
+    private Integer del_flag;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Agreement.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Agreement.java
new file mode 100644
index 0000000..e152cc5
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Agreement.java
@@ -0,0 +1,33 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_agreement")
+public class Agreement {
+
+    @ApiModelProperty("主键")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("类型(1=用户协议,2=隐私协议,3=下单须知,4=注销协议,5=首页介绍,6=快递代拿下单说明,7=商品代买下单说明)")
+    @TableField("type")
+    private Integer type;
+
+    @ApiModelProperty("内容")
+    @TableField("content")
+    private String content;
+
+    @ApiModelProperty("添加时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime create_time;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/AppUser.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/AppUser.java
new file mode 100644
index 0000000..6a7e622
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/AppUser.java
@@ -0,0 +1,98 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_app_user")
+public class AppUser implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("主键")
+    @TableId("id")
+    private Long id;
+
+    @ApiModelProperty("跑腿id")
+    @TableField("courier_id")
+    private Integer courierId;
+
+    @ApiModelProperty("小区id")
+    @TableField("community_id")
+    private Integer communityId;
+
+    @ApiModelProperty("用户")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty("手机号")
+    @TableField("phone")
+    private String phone;
+
+    @ApiModelProperty("头像")
+    @TableField("avatar")
+    private String avatar;
+
+    @ApiModelProperty("性别(1=男,2=女,3=未知)")
+    @TableField("sex")
+    private Integer sex;
+
+    @ApiModelProperty("生日")
+    @TableField("birthday")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate birthday;
+
+    @ApiModelProperty("微信openid")
+    @TableField("wx_openid")
+    private String wxOpenid;
+
+    @ApiModelProperty("支付宝openid")
+    @TableField("ali_openid")
+    private String aliOpenid;
+
+    @ApiModelProperty("状态(1=正常,2=冻结,3=注销)")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty("删除(0=否,1=是)")
+    @TableField("del_flag")
+    private Integer delFlag;
+
+    @ApiModelProperty("第一次登录(0=否,1=是)")
+    @TableField("first_login")
+    private Integer firstLogin;
+
+    @ApiModelProperty("添加时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("会员id")
+    @TableField("vip_id")
+    private Integer vipId;
+
+    @ApiModelProperty("注册会员时间")
+    @TableField("start_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime startTime;
+
+    @ApiModelProperty("会员到期时间")
+    @TableField("end_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endTime;
+
+    @ApiModelProperty("第一次下单:0否1是")
+    @TableField("first_order")
+    private Integer firstOrder;
+
+    @ApiModelProperty("最后一次登录时间")
+    @TableField("last_login_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime lastLoginTime;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Banner.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Banner.java
new file mode 100644
index 0000000..f6b6ca0
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Banner.java
@@ -0,0 +1,59 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_banner")
+public class Banner implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @TableId(type = IdType.AUTO)
+    @ApiModelProperty("主键")
+    private Integer id;
+
+    @ApiModelProperty("位置(1=下单页面)")
+    @TableField("position")
+    private Integer position;
+
+    @ApiModelProperty("名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty("跳转类型(1=无跳转,2=外部链接)")
+    @TableField("jump_type")
+    private Integer jumpType;
+
+    @ApiModelProperty("内容类型")
+    @TableField("content_type")
+    private Integer contentType;
+
+    @ApiModelProperty("跳转内容")
+    @TableField("content")
+    private String content;
+
+    @ApiModelProperty("图片地址")
+    @TableField("image_url")
+    private String imageUrl;
+
+    @ApiModelProperty("视频地址")
+    @TableField("video_url")
+    private String videoUrl;
+
+    @ApiModelProperty("删除标识(0=否,1=是)")
+    @TableField("del_flag")
+    private Integer delFlag;
+
+    @ApiModelProperty("添加时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Community.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Community.java
new file mode 100644
index 0000000..51ef7d8
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Community.java
@@ -0,0 +1,64 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_community")
+public class Community implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("主键ID")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("小区名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty("小区详细地址")
+    @TableField("address")
+    private String address;
+
+    @ApiModelProperty("关联省市区的区域ID")
+    @TableField("region_id")
+    private Integer regionId;
+
+    @ApiModelProperty("管理员名称")
+    @TableField("admin_name")
+    private String adminName;
+
+    @ApiModelProperty("管理员联系电话")
+    @TableField("admin_phone")
+    private String adminPhone;
+
+    @ApiModelProperty("状态:0-正常,1-冻结")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty("删除标志:0-否,1-是")
+    @TableField("del_flag")
+    private Integer delFlag;
+
+    @ApiModelProperty("收费金额(元)")
+    @TableField("fee_amount")
+    private BigDecimal feeAmount;
+
+    @ApiModelProperty("创建时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/CommunityCourier.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/CommunityCourier.java
new file mode 100644
index 0000000..35d2735
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/CommunityCourier.java
@@ -0,0 +1,40 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_community_courier")
+public class CommunityCourier implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("关联ID")
+    @TableField("id")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("小区ID")
+    @TableField("community_id")
+    private Integer communityId;
+
+    @ApiModelProperty("跑腿员ID")
+    @TableField("courier_id")
+    private Integer courierId;
+
+    @ApiModelProperty("创建时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java
new file mode 100644
index 0000000..c544be3
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java
@@ -0,0 +1,63 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_courier")
+public class Courier implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("跑腿员ID")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("姓名")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty("联系电话")
+    @TableField("phone")
+    private String phone;
+
+    @ApiModelProperty("身份证号")
+    @TableField("id_card")
+    private String idCard;
+
+    @ApiModelProperty("头像URL")
+    @TableField("avatar")
+    private String avatar;
+
+    @ApiModelProperty("正面照地址")
+    @TableField("front_view")
+    private String frontView;
+
+    @ApiModelProperty("背面照地址")
+    @TableField("back_view")
+    private String backView;
+
+    @ApiModelProperty("状态:0-禁用,1-启用")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty("创建时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("删除(0=否,1=是)")
+    @TableField("del_flag")
+    private Integer delFlag;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java
new file mode 100644
index 0000000..56fab8e
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java
@@ -0,0 +1,49 @@
+package com.ruoyi.errand.domain;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 评价表实体类
+ */
+@Data
+@TableName("t_evaluation")
+public class Evaluation implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("评价id")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("订单id")
+    @TableField("order_id")
+    private Integer orderId;
+    @ApiModelProperty("评价类型 0-用户评价 1-跑腿员评价")
+    @TableField("type")
+    private Integer type;
+
+    @ApiModelProperty("评分(0.5-5.0,支持半星")
+    @TableField("rating")
+    private BigDecimal rating;
+
+    @ApiModelProperty("评价内容")
+    @TableField("content")
+    private String content;
+
+    @ApiModelProperty("评价时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("修改时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Feedback.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Feedback.java
new file mode 100644
index 0000000..c7e1136
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Feedback.java
@@ -0,0 +1,63 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_feedback")
+public class Feedback implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("反馈ID")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("反馈用户id")
+    @TableField("app_user_id")
+    private Long appUserId;
+
+    @ApiModelProperty("反馈用户名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty("联系电话")
+    @TableField("phone")
+    private String phone;
+
+    @ApiModelProperty("反馈内容")
+    @TableField("content")
+    private String content;
+
+    @ApiModelProperty("反馈时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("状态:0-未处理,1-已处理")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty("处理人ID")
+    @TableField("handler_id")
+    private Long handlerId;
+
+    @ApiModelProperty("处理时间")
+    @TableField("handle_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime handleTime;
+
+    @ApiModelProperty("处理备注")
+    @TableField("handle_remark")
+    private String handleRemark;
+
+    @ApiModelProperty("是否删除:0-未删除,1-已删除")
+    @TableField("del_flag")
+    private Integer delFlag;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Order.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Order.java
new file mode 100644
index 0000000..ac880c0
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Order.java
@@ -0,0 +1,135 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_order")
+public class Order implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("订单id")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("订单编号")
+    @TableField("order_number")
+    private String orderNumber;
+
+    @ApiModelProperty("用户id")
+    @TableField("app_user_id")
+    private Long appUserId;
+
+    @ApiModelProperty("跑腿id")
+    @TableField("courier_id")
+    private Integer courierId;
+
+    @ApiModelProperty("小区id")
+    @TableField("community_id")
+    private Integer communityId;
+
+    @ApiModelProperty("小区名称")
+    @TableField("community_name")
+    private String communityName;
+
+    @ApiModelProperty("收件人姓名")
+    @TableField("recipient_name")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    @TableField("recipient_phone")
+    private String recipientPhone;
+
+    @ApiModelProperty("详细地址")
+    @TableField("address_detail")
+    private String addressDetail;
+
+    @ApiModelProperty("代办事项")
+    @TableField("agency_matters")
+    private String agencyMatters;
+
+    @ApiModelProperty("货品件数")
+    @TableField("num")
+    private Integer num;
+
+    @ApiModelProperty("备注")
+    @TableField("remark")
+    private String remark;
+
+    @ApiModelProperty("上传图片地址(多张逗号隔开,最多五张)")
+    @TableField("pics")
+    private String pics;
+
+    @ApiModelProperty("跑腿上传图片地址(多张逗号隔开,最多五张)")
+    @TableField("courier_pics")
+    private String courierPics;
+
+    @ApiModelProperty("支付方式 0-在线支付 1-会员支付")
+    @TableField("pay_method")
+    private Integer payMethod;
+
+    @ApiModelProperty("订单金额")
+    @TableField("order_amount")
+    private BigDecimal orderAmount;
+
+    @ApiModelProperty("支付金额")
+    @TableField("payment_amount")
+    private BigDecimal paymentAmount;
+
+    @ApiModelProperty("1待确认2进行中3已取消4已完成5已评价")
+    @TableField("order_status")
+    private Integer orderStatus;
+
+    @ApiModelProperty("创建时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("下单时间")
+    @TableField("order_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderTime;
+
+    @ApiModelProperty("接单时间")
+    @TableField("receiving_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime receivingTime;
+
+    @ApiModelProperty("完成时间")
+    @TableField("finish_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime finishTime;
+
+    @ApiModelProperty("微信支付号")
+    @TableField("serial_number")
+    private String serialNumber;
+
+    @ApiModelProperty("1待支付2已支付")
+    @TableField("pay_status")
+    private Integer payStatus;
+    @ApiModelProperty("0否1是 是否删除")
+    @TableField("del_flag")
+    private Integer delFlag;
+
+    @ApiModelProperty("退款状态(1=退款中,2=已退款)")
+    @TableField("refund_status")
+    private Integer refundStatus;
+
+    @ApiModelProperty("退款流水号")
+    @TableField("refund_code")
+    private String refundCode;
+
+    @ApiModelProperty("退款时间")
+    @TableField("refund_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime refundTime;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Phone.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Phone.java
new file mode 100644
index 0000000..37569c5
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Phone.java
@@ -0,0 +1,31 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@TableName("t_phone")
+public class Phone implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("主键")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("类型(1=平台,2=小区)")
+    @TableField("type")
+    private Integer type;
+
+    @ApiModelProperty("小区id")
+    @TableField("community_id")
+    private Integer community_id;
+
+    @ApiModelProperty("联系电话")
+    @TableField("phone")
+    private String phone;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Region.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Region.java
new file mode 100644
index 0000000..14f3789
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Region.java
@@ -0,0 +1,43 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@TableName("t_region")
+public class Region implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty("")
+    @TableField("code")
+    private String code;
+
+    @ApiModelProperty("")
+    @TableField("citycode")
+    private String citycode;
+
+    @ApiModelProperty("")
+    @TableField("parent_id")
+    private Integer parentId;
+
+    @ApiModelProperty("")
+    @TableField("english")
+    private String english;
+    @TableField(exist = false)
+    @ApiModelProperty("")
+    private List<Region> childs;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/Report.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/Report.java
new file mode 100644
index 0000000..2393491
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/Report.java
@@ -0,0 +1,58 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_report")
+public class Report implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("上报id")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("关联省市区的区域id")
+    @TableField("region_id")
+    private Integer regionId;
+
+    @ApiModelProperty("用户ID")
+    @TableField("app_user_id")
+    private Long appUserId;
+
+    @ApiModelProperty("小区名称")
+    @TableField("community_name")
+    private String communityName;
+
+    @ApiModelProperty("详细地址")
+    @TableField("address_detail")
+    private String addressDetail;
+
+    @ApiModelProperty("上报时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("状态 0-未处理1-已处理")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty("是否删除:0-否,1-是")
+    @TableField("del_flag")
+    private Integer delFlag;
+
+    @ApiModelProperty("处理时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+
+
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/SystemConfig.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/SystemConfig.java
new file mode 100644
index 0000000..0e9377d
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/SystemConfig.java
@@ -0,0 +1,26 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@TableName("t_system_config")
+public class SystemConfig implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @TableId(type = IdType.AUTO)
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+    @ApiModelProperty(value = "1=启动页管理")
+    @TableField("type")
+    private Integer type;
+    @ApiModelProperty(value = "JSON配置内容")
+    @TableField("content")
+    private String content;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/UserCancellationLog.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/UserCancellationLog.java
new file mode 100644
index 0000000..0c55986
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/UserCancellationLog.java
@@ -0,0 +1,36 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_user_cancellation_log")
+public class UserCancellationLog implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("主键")
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("删除标志(0=否,1=是)")
+    @TableField("del_flag")
+    private Integer del_flag;
+
+    @ApiModelProperty("用户id")
+    @TableField("app_user_id")
+    private Long app_user_id;
+
+
+    @ApiModelProperty("注销时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime create_time;
+
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/VipOrder.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/VipOrder.java
new file mode 100644
index 0000000..bc3f91e
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/VipOrder.java
@@ -0,0 +1,74 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Data
+@TableName("t_vip_order")
+@ApiModel("vip订单实体类")
+public class VipOrder {
+
+    @ApiModelProperty("vip订单主键")
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("vip的主键id")
+    @TableField("vip_id")
+    private Integer vipId;
+
+    @ApiModelProperty("订单编号")
+    @TableField("order_number")
+    private String orderNumber;
+
+    @ApiModelProperty("用户id")
+    @TableField("app_user_id")
+    private Long appUserId;
+    @ApiModelProperty("4已完成6已退费")
+    @TableField("order_status")
+    private Integer orderStatus;
+
+    @ApiModelProperty("订单金额")
+    @TableField("order_amount")
+    private BigDecimal orderAmount;
+
+    @ApiModelProperty("支付金额")
+    @TableField("payment_amount")
+    private BigDecimal paymentAmount;
+
+    @ApiModelProperty("下单时间")
+    @TableField("order_time")
+    private LocalDateTime orderTime;
+
+    @ApiModelProperty("微信支付号")
+    @TableField("serial_number")
+    private String serialNumber;
+
+    @ApiModelProperty("1待支付2已支付")
+    @TableField("pay_status")
+    private Integer payStatus;
+
+    @ApiModelProperty("退款状态(1=退款中,2=已退款)")
+    @TableField("refund_status")
+    private Integer refundStatus;
+
+    @ApiModelProperty("退款流水号")
+    @TableField("refund_code")
+    private String refundCode;
+
+    @ApiModelProperty("退款时间")
+    @TableField("refund_time")
+    private LocalDateTime refundTime;
+
+    @ApiModelProperty("0否1是 是否删除")
+    @TableField("del_flag")
+    private Integer delFlag;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/domain/VipSetting.java b/pt-errand/src/main/java/com/ruoyi/errand/domain/VipSetting.java
new file mode 100644
index 0000000..81126a1
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/domain/VipSetting.java
@@ -0,0 +1,39 @@
+package com.ruoyi.errand.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_vip_setting")
+public class VipSetting implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @ApiModelProperty("1-4对应会员")
+    private Integer id;
+
+    @ApiModelProperty("会员名称")
+    @TableField("vip_name")
+    private String vip_name;
+
+    @ApiModelProperty("会员价格")
+    @TableField("vip_price")
+    private BigDecimal vip_price;
+
+    @ApiModelProperty("添加时间")
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime create_time;
+
+    @ApiModelProperty("添加时间")
+    @TableField("update_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime update_time;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/interceptor/APPJwtTokenInterceptor.java b/pt-errand/src/main/java/com/ruoyi/errand/interceptor/APPJwtTokenInterceptor.java
new file mode 100644
index 0000000..9d25cea
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/interceptor/APPJwtTokenInterceptor.java
@@ -0,0 +1,126 @@
+package com.ruoyi.errand.interceptor;
+
+
+
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.errand.constant.AppUserStatusConstant;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.mapper.AppUserMapper;
+import com.ruoyi.errand.utils.JwtUtil;
+import com.ruoyi.errand.utils.TokenBlacklistService;
+import com.ruoyi.errand.utils.WeAppAuthenticationToken;
+import com.ruoyi.errand.utils.WeAppSecurityProperties;
+import io.jsonwebtoken.Claims;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * jwt令牌校验的拦截器
+ */
+@Component
+@Slf4j
+public class APPJwtTokenInterceptor  extends OncePerRequestFilter {
+
+    @Autowired
+    private WeAppSecurityProperties weAppSecurityProperties;
+    @Autowired
+    private TokenBlacklistService tokenBlacklistService;
+
+
+
+    @Autowired
+    private AppUserMapper appUserMapper;
+    private static final PathMatcher pathMatcher = new AntPathMatcher();
+    @Override
+    protected void doFilterInternal(HttpServletRequest request,
+                                    HttpServletResponse response,
+                                    FilterChain chain) throws IOException, ServletException {
+        try {
+            String path = request.getRequestURI();
+            log.info("进入appJwt认证");
+            // 检查是否在白名单中
+            if (isExcludedPath(path)) {
+                chain.doFilter(request, response);
+                return;
+            }
+            //不是app开头的也不用校验
+            if (!path.startsWith("/app")) {
+                chain.doFilter(request, response);
+                return;
+            }
+
+
+            // 从Header提取Token(如:Authorization: WeApp {token})
+            String token = request.getHeader("Authorization");
+            if (token == null || !token.startsWith("app:")) {
+                throw new ServiceException("无效token", 401);
+            }
+            //解析token 获取userid,再查询到AppUser
+            String realToken = token.substring(4);
+            //查看是否在黑名单中
+            if (tokenBlacklistService.isBlacklisted(realToken)) {
+                throw new ServiceException("无效token", 401);
+            }
+            try {
+
+                Claims claims = JwtUtil.parseJWT(realToken);
+                String userId = claims.get("userId").toString();
+                AppUser appUser = appUserMapper.selectById(userId);
+                if (appUser == null || appUser.getDelFlag().equals(DelFlagConstant.DELETE)) {
+                    throw new ServiceException("用户不存在", 401);
+                }
+                if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.FREEZE)) {
+                    throw new ServiceException("该账户已被冻结", 401);
+                }
+                if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.LOGOUT)) {
+                    throw new ServiceException("该账户已被注销", 401);
+                }
+                // 将userId存入SecurityContext(需自定义Authentication对象)
+                WeAppAuthenticationToken authentication =
+                        new WeAppAuthenticationToken(userId, appUser, token);
+                SecurityContextHolder.getContext().setAuthentication(authentication);
+                chain.doFilter(request, response);
+            } catch (Exception e) {
+                throw new ServiceException("TOKEN解析失败", 401);
+            }
+        }catch (ServiceException e) {
+            response.setStatus(e.getCode() != null ? e.getCode() : 401);
+            response.setContentType("application/json;charset=UTF-8");
+            response.getWriter().write(
+                    String.format("{\"code\":%d,\"msg\":\"%s\"}",
+                            e.getCode(), e.getMessage())
+            );
+            return;
+        }catch (Exception e) {
+            response.setStatus(401);
+            response.setContentType("application/json;charset=UTF-8");
+            response.getWriter().write(
+                    "{\"code\":500,\"msg\":\"系统内部错误\"}"
+            );
+            return;
+        }
+    }
+    /**
+     * 判断路径是否在白名单中
+     */
+    private boolean isExcludedPath(String requestPath) {
+        return weAppSecurityProperties.getExcludeUrls().stream()
+                .anyMatch(excludedPath ->
+                        pathMatcher.match(excludedPath, requestPath)
+                );
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/AddressBookMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/AddressBookMapper.java
new file mode 100644
index 0000000..e85a682
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/AddressBookMapper.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.AddressBook;
+import com.ruoyi.errand.object.vo.app.AddressBookByCommunityIdVO;
+import com.ruoyi.errand.object.vo.app.AddressBookListVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface AddressBookMapper extends BaseMapper<AddressBook> {
+    List<AddressBookByCommunityIdVO> addressBookByCommunityId(@Param("communityId") Integer communityId,@Param("userId")Long userId);
+
+    List<AddressBookListVO> addressBookList(@Param("userId") Long userId);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/AgreementMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/AgreementMapper.java
new file mode 100644
index 0000000..5fdbacd
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/AgreementMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.Agreement;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface AgreementMapper extends BaseMapper<Agreement> {
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/AppUserMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/AppUserMapper.java
new file mode 100644
index 0000000..61d83f2
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/AppUserMapper.java
@@ -0,0 +1,34 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.object.dto.sys.AppUserPageListDTO;
+import com.ruoyi.errand.object.vo.app.AppUserInfoVO;
+import com.ruoyi.errand.object.vo.app.OrderPageVO;
+import com.ruoyi.errand.object.vo.sys.AppUserPageListVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+@Mapper
+public interface AppUserMapper extends BaseMapper<AppUser> {
+    List<OrderPageVO> getOrderPage(@Param("userId") Long userId);
+
+    AppUserInfoVO getMyInfo(@Param("id") Long id);
+
+    Integer countByCreateTimeBefore(@Param("end") LocalDateTime end);
+
+    Integer countByCreateTimeBetween(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end);
+
+    List<Map<String, Object>> countGroupByDate(@Param("start") LocalDateTime start,
+                                               @Param("end") LocalDateTime end,
+                                               @Param("datePattern") String datePattern);
+
+    Map<String,Object> getCourierByCommunityId(@Param("communityId")Integer communityId);
+
+    IPage<AppUserPageListVO> getAppUserPageList(@Param("page")IPage<AppUserPageListVO> page, @Param("dto")AppUserPageListDTO dto);
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/BannerMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/BannerMapper.java
new file mode 100644
index 0000000..898238a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/BannerMapper.java
@@ -0,0 +1,21 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.errand.domain.Banner;
+import com.ruoyi.errand.object.vo.app.BannerVO;
+import com.ruoyi.errand.object.vo.sys.BannerPageListVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface BannerMapper extends BaseMapper<Banner> {
+    List<BannerVO> getBannerList();
+
+    IPage<BannerPageListVO> pageList(@Param("page") IPage<BannerPageListVO> page,@Param("name") String name);
+}
+
+
+
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityCourierMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityCourierMapper.java
new file mode 100644
index 0000000..a22bb27
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityCourierMapper.java
@@ -0,0 +1,14 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.CommunityCourier;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface CommunityCourierMapper extends BaseMapper<CommunityCourier> {
+    List<Integer> getAllCourierList();
+
+    List<Integer> getAllCommunityList();
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityMapper.java
new file mode 100644
index 0000000..60934b5
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/CommunityMapper.java
@@ -0,0 +1,26 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+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.CommunityPageListDTO;
+import com.ruoyi.errand.object.vo.app.CommunityListVO;
+import com.ruoyi.errand.object.vo.sys.AllCommunityListVO;
+import com.ruoyi.errand.object.vo.sys.CommunityPageListVO;
+import com.ruoyi.errand.object.vo.sys.CourierPageListVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface CommunityMapper extends BaseMapper<Community> {
+    List<CommunityListVO> selectListByRegionId(@Param("regionId") Integer regionId);
+
+    List<CommunityListVO> getTotalCommunityList();
+
+    List<AllCommunityListVO> getAllCommunityList(@Param("list")List<Integer> list);
+
+    IPage<CommunityPageListVO> getCommunityPageList(@Param("page")IPage<CommunityPageListVO> page, @Param("dto")CommunityPageListDTO dto);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/CourierMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/CourierMapper.java
new file mode 100644
index 0000000..861118e
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/CourierMapper.java
@@ -0,0 +1,32 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.errand.domain.Courier;
+import com.ruoyi.errand.object.dto.sys.CourierPageListDTO;
+import com.ruoyi.errand.object.vo.app.CourierInfoVO;
+import com.ruoyi.errand.object.vo.app.CourierOrderListVO;
+import com.ruoyi.errand.object.vo.app.CourierStatisticsVO;
+import com.ruoyi.errand.object.vo.sys.AllCourierListVO;
+import com.ruoyi.errand.object.vo.sys.CourierPageListVO;
+import com.ruoyi.errand.object.vo.sys.CourierSysDetailVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface CourierMapper extends BaseMapper<Courier> {
+    CourierInfoVO getCourierInfo(@Param("courierId") Integer courierId);
+
+    CourierStatisticsVO getDatStatistics(Integer courierId);
+
+    IPage<CourierOrderListVO> getCourierOrderList(@Param("page") IPage<CourierOrderListVO> page, @Param("orderStatus") Integer orderStatus,@Param("courierId")  Integer courierId);
+
+    IPage<CourierPageListVO> getCourierPageList(@Param("page") IPage<CourierPageListVO> page, @Param("dto") CourierPageListDTO dto);
+
+    CourierSysDetailVO detail(@Param("id")Integer id);
+
+
+    List<AllCourierListVO> getAllCourierList(@Param("couriers")List<Integer> couriers);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/EvaluationMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/EvaluationMapper.java
new file mode 100644
index 0000000..0167453
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/EvaluationMapper.java
@@ -0,0 +1,11 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.Evaluation;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface EvaluationMapper extends BaseMapper<Evaluation> {
+    Integer getFiveCount(@Param("courierId") Integer courierId);
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/FeedbackMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/FeedbackMapper.java
new file mode 100644
index 0000000..1d1d4de
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/FeedbackMapper.java
@@ -0,0 +1,14 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.errand.domain.Feedback;
+import com.ruoyi.errand.object.dto.sys.FeedbackPageListDTO;
+import com.ruoyi.errand.object.vo.sys.FeedbackPageListVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface FeedbackMapper extends BaseMapper<Feedback> {
+    IPage<FeedbackPageListVO> getFeedbackPageList(@Param("page") IPage<FeedbackPageListVO> page, @Param("dto")FeedbackPageListDTO dto);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/OrderMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/OrderMapper.java
new file mode 100644
index 0000000..ce63953
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/OrderMapper.java
@@ -0,0 +1,43 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.errand.domain.Order;
+import com.ruoyi.errand.object.dto.sys.FinanceStatisticsDTO;
+import com.ruoyi.errand.object.dto.sys.OrderPageListDTO;
+import com.ruoyi.errand.object.vo.app.AppUserOrderListVO;
+import com.ruoyi.errand.object.vo.app.OrderDetailVO;
+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 org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.security.core.parameters.P;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+@Mapper
+public interface OrderMapper extends BaseMapper<Order> {
+    IPage<AppUserOrderListVO> getAppUserOrderList(@Param("page") IPage<Order> page, @Param("orderStatus") Integer orderStatus, @Param("appUserId")Long appUserId);
+
+    OrderDetailVO getOrderDetail(Integer id);
+
+    Map<String, Object> getOrderTopInfoByDate(@Param("communityId")Integer communityId,@Param("start") LocalDateTime start, @Param("end")LocalDateTime end);
+
+    List<Map<String, Object>> countGroupByDate(@Param("start") LocalDateTime start,@Param("end")  LocalDateTime end,@Param("datePattern")  String datePattern, @Param("communityId") Integer communityId);
+
+    List<FinanceStatisticsVO> financeStatistics( @Param("dto") FinanceStatisticsDTO dto);
+
+    long selectPageTotal(@Param("dto") FinanceStatisticsDTO dto);
+
+    IPage<OrderPageListVO> getOrderPageList(@Param("page")IPage<OrderPageListVO> page, @Param("dto")OrderPageListDTO dto);
+
+    OrderSysDetailVO detail(@Param("id")Integer id);
+
+    List<FinanceStatisticsVO> export(@Param("dto")FinanceStatisticsDTO dto);
+
+    List<OrderPageListVO> orderExport(@Param("dto")OrderPageListDTO dto);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/PhoneMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/PhoneMapper.java
new file mode 100644
index 0000000..594fa9b
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/PhoneMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.Phone;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PhoneMapper extends BaseMapper<Phone> {
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/RegionMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/RegionMapper.java
new file mode 100644
index 0000000..fad0463
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/RegionMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.Region;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface RegionMapper extends BaseMapper<Region> {
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/ReportMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/ReportMapper.java
new file mode 100644
index 0000000..c2afdc1
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/ReportMapper.java
@@ -0,0 +1,15 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.errand.domain.AddressBook;
+import com.ruoyi.errand.domain.Report;
+import com.ruoyi.errand.object.dto.sys.ReportPageListDTO;
+import com.ruoyi.errand.object.vo.sys.ReportPageListVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface ReportMapper extends BaseMapper<Report> {
+    IPage<ReportPageListVO> getReportList(@Param("page") IPage<ReportPageListVO> page, @Param("dto") ReportPageListDTO dto);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/SystemConfigMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/SystemConfigMapper.java
new file mode 100644
index 0000000..670750c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/SystemConfigMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.SystemConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SystemConfigMapper extends BaseMapper<SystemConfig> {
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/UserCancellationLogMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/UserCancellationLogMapper.java
new file mode 100644
index 0000000..70e7482
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/UserCancellationLogMapper.java
@@ -0,0 +1,10 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.Agreement;
+import com.ruoyi.errand.domain.UserCancellationLog;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UserCancellationLogMapper extends BaseMapper<UserCancellationLog> {
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/VipOrderMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/VipOrderMapper.java
new file mode 100644
index 0000000..f626fd4
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/VipOrderMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.UserCancellationLog;
+import com.ruoyi.errand.domain.VipOrder;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface VipOrderMapper extends BaseMapper<VipOrder> {
+    List<VipOrder> selectRefundVipOrder(@Param("appUserId") Integer appUserId);
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/mapper/VipSettingMapper.java b/pt-errand/src/main/java/com/ruoyi/errand/mapper/VipSettingMapper.java
new file mode 100644
index 0000000..66a8ebe
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/mapper/VipSettingMapper.java
@@ -0,0 +1,14 @@
+package com.ruoyi.errand.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.errand.domain.Agreement;
+import com.ruoyi.errand.domain.VipSetting;
+import com.ruoyi.errand.object.vo.app.VipInfoListVO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface VipSettingMapper extends BaseMapper<VipSetting> {
+    List<VipInfoListVO> getVipInfoList();
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddAddressBookDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddAddressBookDTO.java
new file mode 100644
index 0000000..e763c9e
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddAddressBookDTO.java
@@ -0,0 +1,18 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("添加新地址")
+public class AddAddressBookDTO {
+    @ApiModelProperty("关联小区ID")
+    private Integer communityId;
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java
new file mode 100644
index 0000000..618f579
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java
@@ -0,0 +1,31 @@
+package com.ruoyi.errand.object.dto.app;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.*;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("评价订单")
+public class AddEvaluationDTO {
+    @ApiModelProperty("订单id")
+    @NotNull(message = "订单id不能为空")
+    private Integer orderId;
+
+    @ApiModelProperty("评价类型 0-用户评价 1-跑腿员评价")
+    @NotNull(message = "评价类型不能为空")
+    private Integer type;
+
+    @ApiModelProperty("评分(0.5-5.0,支持半星")
+    @NotNull(message = "评分不能为空")
+    @DecimalMin(value = "0.5", message = "评分最低只能为0.5")
+    @DecimalMax(value = "5.0", message = "评分最高只能为5.0")
+    @Digits(integer = 1, fraction = 1, message = "评分必须是一位小数(如3.5)")
+    private BigDecimal rating;
+
+    @ApiModelProperty("评价内容")
+    private String content;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddReportDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddReportDTO.java
new file mode 100644
index 0000000..5ae3e08
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddReportDTO.java
@@ -0,0 +1,23 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("上报不存在小区")
+public class AddReportDTO {
+    @ApiModelProperty("关联省市区的区域id")
+    @NotNull(message = "上报区域不能为空")
+    private Integer region_id;
+    @ApiModelProperty("小区名称")
+    @NotEmpty(message = "小区名称不能为空")
+    private String communityName;
+    @ApiModelProperty("详细地址")
+    @NotEmpty(message = "详细地址不能为空")
+    private String addressDetail;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AgreementDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AgreementDTO.java
new file mode 100644
index 0000000..8d4fef3
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AgreementDTO.java
@@ -0,0 +1,16 @@
+package com.ruoyi.errand.object.dto.app;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("协议DTO")
+public class AgreementDTO {
+    @ApiModelProperty("类型(1=用户协议,2=隐私协议,3=下单须知,4=注销协议,5=首页介绍,6=快递代拿下单说明,7=商品代买下单说明)")
+    private Integer type;
+
+    @ApiModelProperty("内容")
+    private String content;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AppletLogin.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AppletLogin.java
new file mode 100644
index 0000000..22e24bb
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AppletLogin.java
@@ -0,0 +1,16 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("一键登录")
+public class AppletLogin {
+	@ApiModelProperty(value = "微信jscode", required = true)
+	private String jscode;
+	@ApiModelProperty(value = "手机号加密数据", required = true)
+	private String encryptedData_phone;
+	@ApiModelProperty(value = "加密算法的初始向量", required = true)
+	private String iv_phone;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/BirthDayDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/BirthDayDTO.java
new file mode 100644
index 0000000..bc11a1c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/BirthDayDTO.java
@@ -0,0 +1,16 @@
+package com.ruoyi.errand.object.dto.app;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Data
+@ApiModel("生日")
+public class BirthDayDTO {
+    @ApiModelProperty("生日")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate birthday;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/ConfirmOrderDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/ConfirmOrderDTO.java
new file mode 100644
index 0000000..7993430
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/ConfirmOrderDTO.java
@@ -0,0 +1,37 @@
+package com.ruoyi.errand.object.dto.app;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("确认订单DTO")
+public class ConfirmOrderDTO {
+    @ApiModelProperty("小区id")
+    @NotNull(message = "小区id不能为空")
+    private Integer commodityId;
+
+    @ApiModelProperty("支付方式 0-在线支付 1-会员支付")
+    @NotNull(message = "支付方式不能为空")
+    private Integer payMethod;
+
+    @ApiModelProperty("地址簿id")
+    @NotNull(message = "地址簿id不能为空")
+    private Integer addressBookId;
+
+
+    @ApiModelProperty("代办事项")
+    @NotEmpty(message = "代办事项不能为空")
+    private String agencyMatters;
+    @ApiModelProperty("货品件数")
+    private Integer num;
+    @ApiModelProperty("备注")
+    private String remark;
+    @ApiModelProperty("上传图片地址(多张逗号隔开,最多五张)")
+    @TableField("pics")
+    private String pics;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/MobileLoginDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/MobileLoginDTO.java
new file mode 100644
index 0000000..c71f52c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/MobileLoginDTO.java
@@ -0,0 +1,21 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+
+@Data
+@ApiModel("手机号登录DTO")
+public class MobileLoginDTO {
+	@ApiModelProperty("手机号")
+	@NotEmpty(message = "手机号不能为空")
+	private String phone;
+	@ApiModelProperty("短信验证码")
+	@NotEmpty(message = "短信验证码不能为空")
+	private String code;
+	@ApiModelProperty(value = "微信jscode", required = true)
+	private String jscode;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderPayment.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderPayment.java
new file mode 100644
index 0000000..0009737
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderPayment.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel
+public class OrderPayment {
+	@ApiModelProperty(value = "商品id", required = true)
+	private Integer goodId;
+
+	@ApiModelProperty(value = "支付方式(1=微信,2-会员)", required = true)
+	private Integer paymentType;
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsDTO.java
new file mode 100644
index 0000000..013e6ef
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsDTO.java
@@ -0,0 +1,28 @@
+package com.ruoyi.errand.object.dto.app;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDate;
+
+@Data
+@ApiModel("订单统计")
+public class OrderStatsDTO {
+    @ApiModelProperty("筛选类型 1-今日 2-本周 3-本月 4-本季度 5-半年 6-本年 7-自定义")
+    @NotNull(message = "筛选类型不能为空")
+    private Integer type;
+
+    @ApiModelProperty("小区id")
+    private Integer communityId=0;
+
+    @ApiModelProperty("自定义起始时间")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate startDate; // 自定义时间范围使用
+
+    @ApiModelProperty("自定义结束时间")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endDate;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsVO.java
new file mode 100644
index 0000000..8c67832
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/OrderStatsVO.java
@@ -0,0 +1,20 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel("订单统计-折线图")
+public class OrderStatsVO {
+    @ApiModelProperty("日期")
+    private List<String> date;
+    @ApiModelProperty("订单统计")
+    private List<Integer> numList;
+    @ApiModelProperty("金额统计")
+    private List<BigDecimal> amountList;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/RegisterDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/RegisterDTO.java
new file mode 100644
index 0000000..9eaa82c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/RegisterDTO.java
@@ -0,0 +1,18 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("用户注册")
+public class RegisterDTO {
+    @ApiModelProperty("用户名")
+    @NotNull(message = "用户名不能为空")
+    private String username;
+    @ApiModelProperty("小区id")
+    @NotNull(message = "必须绑定小区")
+    private Integer communityId;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/SetConfirmOrderDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/SetConfirmOrderDTO.java
new file mode 100644
index 0000000..54eeea3
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/SetConfirmOrderDTO.java
@@ -0,0 +1,29 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+@Getter
+@Setter
+@ApiModel("编辑确认的订单")
+public class SetConfirmOrderDTO{
+    @ApiModelProperty("订单id")
+    @NotNull(message = "订单id不能为空")
+    private Integer id;
+
+    @ApiModelProperty("地址簿id")
+    private Integer addressBookId;
+
+    @ApiModelProperty("代办事项")
+    private String agencyMatters;
+    @ApiModelProperty("货品件数")
+    private Integer num;
+    @ApiModelProperty("备注")
+    private String remark;
+    @ApiModelProperty("上传图片地址(多张逗号隔开,最多五张)")
+    private String pics;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/StartPageSetDto.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/StartPageSetDto.java
new file mode 100644
index 0000000..752ff7d
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/StartPageSetDto.java
@@ -0,0 +1,27 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("启动页DTO")
+public class StartPageSetDto {
+
+    @ApiModelProperty("启动页倒计时")
+    @NotNull(message = "启动页倒计时不能为空")
+    private Integer waitTime;
+    @ApiModelProperty("1图片2视频")
+    @NotNull(message = "类型不能为空")
+    private Integer type;
+    @ApiModelProperty("对应文件地址")
+    @NotEmpty(message = "对应文件地址不能为空")
+    private String url;
+
+
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/UpdateAddressBookDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/UpdateAddressBookDTO.java
new file mode 100644
index 0000000..ba6b335
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/UpdateAddressBookDTO.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+@Getter
+@Setter
+@ApiModel("修改地址信息")
+public class UpdateAddressBookDTO extends AddAddressBookDTO{
+    @ApiModelProperty("地址ID")
+    @NotNull(message = "地址id不能为空")
+    private Integer id;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/VipPaymentDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/VipPaymentDTO.java
new file mode 100644
index 0000000..4edac22
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/VipPaymentDTO.java
@@ -0,0 +1,15 @@
+package com.ruoyi.errand.object.dto.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("购买vip")
+public class VipPaymentDTO {
+    @ApiModelProperty("VIP主键id")
+    @NotNull(message = "VIP主键id不能为空")
+    private Integer id;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddBannerDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddBannerDTO.java
new file mode 100644
index 0000000..804f104
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddBannerDTO.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("添加bannerDTO")
+public class AddBannerDTO {
+    @ApiModelProperty("名称")
+    private String name;
+
+    @ApiModelProperty("图片地址")
+    private String imageUrl;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCommunityDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCommunityDTO.java
new file mode 100644
index 0000000..1ea4fe7
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCommunityDTO.java
@@ -0,0 +1,29 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("添加小区VO")
+public class AddCommunityDTO {
+    @ApiModelProperty("小区名称")
+    private String name;
+    @ApiModelProperty("小区详细地址")
+    private String address;
+    @ApiModelProperty("关联省市区的区域ID")
+    private Integer regionId;
+    @ApiModelProperty("跑腿员id")
+    private Integer courierId;
+    @ApiModelProperty("管理员名称")
+    private String adminName;
+    @ApiModelProperty("管理员联系电话")
+    private String adminPhone;
+    @ApiModelProperty("收费金额(元)")
+    private BigDecimal feeAmount;
+    @ApiModelProperty("客服电话")
+    private String servicePhone;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java
new file mode 100644
index 0000000..d8292c3
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java
@@ -0,0 +1,30 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("添加跑腿员")
+public class AddCourierDTO {
+
+
+    @ApiModelProperty("姓名")
+    private String name;
+
+    @ApiModelProperty("联系电话")
+    private String phone;
+
+    @ApiModelProperty("身份证号")
+    private String idCard;
+
+    @ApiModelProperty("小区ID")
+    private Integer communityId;
+
+    @ApiModelProperty("正面照地址")
+    private String frontView;
+
+    @ApiModelProperty("背面照地址")
+    private String backView;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AppUserPageListDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AppUserPageListDTO.java
new file mode 100644
index 0000000..08c9775
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AppUserPageListDTO.java
@@ -0,0 +1,28 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("app用户分页DTO")
+public class AppUserPageListDTO {
+    @ApiModelProperty("页码")
+    private Integer pageNum=0;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize=10;
+
+    @ApiModelProperty("用户")
+    private String name;
+
+    @ApiModelProperty("手机号")
+    private String phone;
+
+    @ApiModelProperty("是否会员 0否 1是")
+    private Integer isVip;
+
+    @ApiModelProperty("状态(1=正常,2=冻结)")
+    private Integer status;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CommunityPageListDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CommunityPageListDTO.java
new file mode 100644
index 0000000..4790ae7
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CommunityPageListDTO.java
@@ -0,0 +1,35 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("小区管理分页DTO")
+public class CommunityPageListDTO {
+    @ApiModelProperty("页码")
+    private Integer pageNum=0;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize=10;
+
+    @ApiModelProperty("小区名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty("所在城市id")
+    private Integer regionId;
+
+    @ApiModelProperty("管理员名称")
+    private String adminName;
+
+    @ApiModelProperty("管理员联系电话")
+    private String adminPhone;
+
+    @ApiModelProperty("绑定跑腿员")
+    private String courierName;
+
+    @ApiModelProperty("状态:0-正常,1-冻结")
+    private Integer status;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CourierPageListDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CourierPageListDTO.java
new file mode 100644
index 0000000..0306667
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/CourierPageListDTO.java
@@ -0,0 +1,27 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("跑腿员管理分页")
+public class CourierPageListDTO {
+    @ApiModelProperty("页码")
+    private Integer pageNum=0;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize=10;
+
+    @ApiModelProperty("姓名")
+    private String name;
+
+    @ApiModelProperty("联系电话")
+    private String phone;
+
+    @ApiModelProperty("状态:0-离职,1-复职")
+    @TableField("status")
+    private Integer status;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCommunityDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCommunityDTO.java
new file mode 100644
index 0000000..ef19db8
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCommunityDTO.java
@@ -0,0 +1,18 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@ApiModel("编辑小区DTO")
+public class EditCommunityDTO extends AddCommunityDTO {
+    @ApiModelProperty("小区id")
+    private Integer id;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCourierDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCourierDTO.java
new file mode 100644
index 0000000..cd1f402
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/EditCourierDTO.java
@@ -0,0 +1,16 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@ApiModel("编辑跑腿员")
+public class EditCourierDTO extends AddCourierDTO {
+    @ApiModelProperty("跑腿员ID")
+    private Integer id;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FeedbackPageListDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FeedbackPageListDTO.java
new file mode 100644
index 0000000..27637a8
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FeedbackPageListDTO.java
@@ -0,0 +1,23 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("用户反馈分页DTO")
+public class FeedbackPageListDTO {
+    @ApiModelProperty("页码")
+    private Integer pageNum=0;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize=10;
+    @ApiModelProperty("反馈用户名称")
+    private String name;
+    @ApiModelProperty("联系电话")
+    private String phone;
+    @ApiModelProperty("状态:0-未处理,1-已处理")
+    private Integer status;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FinanceStatisticsDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FinanceStatisticsDTO.java
new file mode 100644
index 0000000..9941c44
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/FinanceStatisticsDTO.java
@@ -0,0 +1,45 @@
+package com.ruoyi.errand.object.dto.sys;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("财务统计DTO")
+public class FinanceStatisticsDTO {
+
+    @ApiModelProperty("页码")
+    private Integer pageNum=0;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize=10;
+
+    @ApiModelProperty("用户姓名")
+    private String appUserName;
+
+    @ApiModelProperty("联系电话")
+    private String phone;
+
+    @ApiModelProperty("类型 0-全部 1-订单 2-会员充值")
+    private Integer type;
+
+    @ApiModelProperty("0全部1待确认2进行中3已取消4已完成、已评价6已退费")
+    private Integer orderStatus;
+
+
+    @ApiModelProperty("开始时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime startTime;
+
+    @ApiModelProperty("结束时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endTime;
+
+
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/OrderPageListDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/OrderPageListDTO.java
new file mode 100644
index 0000000..0654cc9
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/OrderPageListDTO.java
@@ -0,0 +1,58 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("订单管理分页")
+public class OrderPageListDTO {
+
+    @ApiModelProperty("页码")
+    private Integer pageNum=0;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize=10;
+
+    @ApiModelProperty("订单编号")
+    private String orderNumber;
+
+    @ApiModelProperty("所在小区")
+    private String communityName;
+
+    @ApiModelProperty("下单用户")
+    private String appUserName;
+
+    @ApiModelProperty("联系电话")
+    private String appUserPhone;
+
+    @ApiModelProperty("跑腿员")
+    private String courierName;
+
+    @ApiModelProperty("跑腿员联系电话")
+    private String courierPhone;
+
+    @ApiModelProperty("下单时间-开始")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderStartTime;
+
+    @ApiModelProperty("下单时间-结束")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderEndTime;
+
+    @ApiModelProperty("完成时间-开始")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime finishStartTime;
+
+
+    @ApiModelProperty("完成时间-结束")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime finishEndTime;
+
+    @ApiModelProperty("1待确认2进行中3已取消 4已完成、已评价")
+    private Integer orderStatus;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/ReportPageListDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/ReportPageListDTO.java
new file mode 100644
index 0000000..ab6ee2d
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/ReportPageListDTO.java
@@ -0,0 +1,31 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("未开通上报分页DTO")
+public class ReportPageListDTO {
+    @ApiModelProperty("页码")
+    private Integer pageNum=0;
+
+    @ApiModelProperty("分页大小")
+    private Integer pageSize=10;
+
+    @ApiModelProperty("小区名称")
+    private String communityName;
+
+    @ApiModelProperty("所在城市:关联省市区的区域id")
+    private Integer regionId;
+
+    @ApiModelProperty("上报用户")
+    private String  appUserName;
+
+    @ApiModelProperty("联系电话")
+    private String appUserPhone;
+
+    @ApiModelProperty("状态 0-未处理1-已处理")
+    private Integer status;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/SetPriceDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/SetPriceDTO.java
new file mode 100644
index 0000000..1f10a76
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/SetPriceDTO.java
@@ -0,0 +1,22 @@
+package com.ruoyi.errand.object.dto.sys;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("设置会员价格")
+public class SetPriceDTO {
+    @ApiModelProperty("1-4对应会员 1-月卡 2-季卡 3-半年卡 4-年卡")
+    @Max(value = 4,message = "id取值在1-4")
+    @Min(value = 1,message = "id取值在1-4")
+    private Integer id;
+
+    @ApiModelProperty("会员价格")
+    private BigDecimal vip_price;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/UserStatsDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/UserStatsDTO.java
new file mode 100644
index 0000000..7c3917a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/UserStatsDTO.java
@@ -0,0 +1,28 @@
+package com.ruoyi.errand.object.dto.sys;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.sql.DataSource;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("用户统计")
+public class UserStatsDTO {
+    @ApiModelProperty("筛选类型 1-今日 2-本周 3-本月 4-本季度 5-半年 6-本年 7-自定义")
+    @NotNull(message = "筛选类型不能为空")
+    private Integer type;
+
+    @ApiModelProperty("自定义起始时间")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate startDate; // 自定义时间范围使用
+
+    @ApiModelProperty("自定义结束时间")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endDate;
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookByCommunityIdVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookByCommunityIdVO.java
new file mode 100644
index 0000000..27e9a8f
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookByCommunityIdVO.java
@@ -0,0 +1,29 @@
+package com.ruoyi.errand.object.vo.app;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("根据小区id查看地址簿列表")
+public class AddressBookByCommunityIdVO {
+
+    @ApiModelProperty("地址ID")
+    private Integer id;
+
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+
+    @ApiModelProperty("是否默认地址:0-否,1-是")
+    private Integer is_default;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookListVO.java
new file mode 100644
index 0000000..ebc3f6b
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AddressBookListVO.java
@@ -0,0 +1,33 @@
+package com.ruoyi.errand.object.vo.app;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("用户地址簿列表")
+public class AddressBookListVO {
+
+    @ApiModelProperty("地址ID")
+    private Integer id;
+
+    @ApiModelProperty("关联小区ID")
+    private Integer communityId;
+
+    @ApiModelProperty("小区名称")
+    private Integer communityName;
+
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+
+    @ApiModelProperty("是否默认地址:0-否,1-是")
+    private Integer is_default;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserInfoVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserInfoVO.java
new file mode 100644
index 0000000..a1f8fad
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserInfoVO.java
@@ -0,0 +1,36 @@
+package com.ruoyi.errand.object.vo.app;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@ApiModel("用户个人中心信息")
+@Data
+public class AppUserInfoVO {
+    @ApiModelProperty("头像")
+    private String avatar;
+
+    @ApiModelProperty("用户")
+    private String name;
+
+    @ApiModelProperty("手机号")
+    private String phone;
+
+    @ApiModelProperty("性别(1=男,2=女,3=未知)")
+    private Integer sex;
+
+    @ApiModelProperty("会员id")
+    private Integer vipId;
+
+    @ApiModelProperty("会员id")
+    private Integer vipName;
+
+    @ApiModelProperty("会员到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endTime;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserOrderListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserOrderListVO.java
new file mode 100644
index 0000000..873b91a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/AppUserOrderListVO.java
@@ -0,0 +1,60 @@
+package com.ruoyi.errand.object.vo.app;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("用户订单列表")
+public class AppUserOrderListVO {
+    @ApiModelProperty("订单id")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("详细地址")
+    @TableField("address_detail")
+    private String addressDetail;
+
+    @ApiModelProperty("收件人姓名")
+    @TableField("recipient_name")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    @TableField("recipient_phone")
+    private String recipientPhone;
+
+    @ApiModelProperty("1待确认2进行中3已取消4已完成")
+    @TableField("order_status")
+    private Integer orderStatus;
+
+    @ApiModelProperty("下单时间")
+    @TableField("order_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderTime;
+
+    @ApiModelProperty("评分(0.5-5.0,支持半星")
+    @TableField("rating")
+    private BigDecimal rating;
+
+    @ApiModelProperty("跑腿id")
+    @TableField("courier_id")
+    private Integer courierId;
+
+    @ApiModelProperty("跑腿人名称")
+    @TableField("courier_name")
+    private Integer courierName;
+
+    @ApiModelProperty("跑腿人手机号")
+    @TableField("courier_phone")
+    private Integer courierPhone;
+
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/BannerVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/BannerVO.java
new file mode 100644
index 0000000..0773047
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/BannerVO.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.object.vo.app;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("查看banner")
+public class BannerVO {
+    @ApiModelProperty("主键")
+    private String id;
+    @TableField("name")
+    private String name;
+    @ApiModelProperty("图片地址")
+    private String image_url;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CommunityListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CommunityListVO.java
new file mode 100644
index 0000000..3164a76
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CommunityListVO.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.object.vo.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiParam;
+import lombok.Data;
+
+@Data
+@ApiModel("小区列表VO")
+public class CommunityListVO {
+    @ApiModelProperty("小区id")
+    private Integer id;
+    @ApiModelProperty("小区名称")
+    private String name;
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CompleteOrderDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CompleteOrderDTO.java
new file mode 100644
index 0000000..2a4a9a5
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CompleteOrderDTO.java
@@ -0,0 +1,29 @@
+package com.ruoyi.errand.object.vo.app;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("完成订单")
+public class CompleteOrderDTO {
+    @ApiModelProperty("订单id")
+    @NotNull(message = "订单id不能为空")
+    private Integer id;
+
+    @ApiModelProperty("跑腿上传图片地址(多张逗号隔开,最多五张)")
+    @NotEmpty(message = "上传图片不能为空")
+    private String courierPics;
+
+    @ApiModelProperty("评分(0.5-5.0,支持半星")
+    private BigDecimal rating;
+
+    @ApiModelProperty("评价内容")
+    private String content;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/ConfirmOrderVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/ConfirmOrderVO.java
new file mode 100644
index 0000000..7c607b7
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/ConfirmOrderVO.java
@@ -0,0 +1,44 @@
+package com.ruoyi.errand.object.vo.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("确认订单页面VO")
+public class ConfirmOrderVO {
+    @ApiModelProperty("订单id")
+    private Integer id;
+    @ApiModelProperty("小区id")
+    private Integer communityId;
+    @ApiModelProperty("小区名称")
+    private String communityName;
+
+    @ApiModelProperty("地址簿id")
+    private Integer addressBookId;
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+
+    @ApiModelProperty("代办事项")
+    private String agencyMatters;
+    @ApiModelProperty("货品件数")
+    private Integer num;
+    @ApiModelProperty("备注")
+    private String remark;
+    @ApiModelProperty("上传图片地址(多张逗号隔开,最多五张)")
+    private String pics;
+
+    @ApiModelProperty("支付方式 0-在线支付 1-会员支付")
+    private Integer payMethod;
+    @ApiModelProperty("支付金额")
+    private BigDecimal paymentAmount;
+    @ApiModelProperty("订单金额")
+    private BigDecimal orderAmount;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierInfoVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierInfoVO.java
new file mode 100644
index 0000000..6d06fcd
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierInfoVO.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.object.vo.app;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("跑腿员基础信息")
+public class CourierInfoVO {
+    @ApiModelProperty("姓名")
+    private String name;
+    @ApiModelProperty("联系电话")
+    private String phone;
+    @ApiModelProperty("小区名称")
+    private Integer communityName;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierOrderListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierOrderListVO.java
new file mode 100644
index 0000000..684b2ab
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierOrderListVO.java
@@ -0,0 +1,37 @@
+package com.ruoyi.errand.object.vo.app;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("跑腿人员订单列表")
+public class CourierOrderListVO {
+    @ApiModelProperty("订单id")
+    private Integer id;
+
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+
+    @ApiModelProperty("1待确认2进行中3已取消4已完成5已评价")
+    private Integer orderStatus;
+
+    @ApiModelProperty("接单时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime receivingTime;
+
+    @ApiModelProperty("评分(0.5-5.0,支持半星")
+    private BigDecimal rating;
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierStatisticsVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierStatisticsVO.java
new file mode 100644
index 0000000..b108371
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/CourierStatisticsVO.java
@@ -0,0 +1,19 @@
+package com.ruoyi.errand.object.vo.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("数据统计")
+public class CourierStatisticsVO {
+    @ApiModelProperty("今日接单量")
+    private Integer day;
+    @ApiModelProperty("本周接单量")
+    private Integer week;
+    @ApiModelProperty("本月接单量")
+    private Integer month;
+    @ApiModelProperty("五星好评")
+    private Integer five;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderDetailVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderDetailVO.java
new file mode 100644
index 0000000..0a8b5a0
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderDetailVO.java
@@ -0,0 +1,76 @@
+package com.ruoyi.errand.object.vo.app;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("订单详情VO")
+public class OrderDetailVO {
+    @ApiModelProperty("订单id")
+    private Integer id;
+    @ApiModelProperty("小区id")
+    private Integer communityId;
+
+    @ApiModelProperty("小区名称")
+    private String communityName;
+
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+    @ApiModelProperty("代办事项")
+    private String agencyMatters;
+
+    @ApiModelProperty("货品件数")
+    private Integer num;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @ApiModelProperty("上传图片地址(多张逗号隔开,最多五张)")
+    private String pics;
+    @ApiModelProperty("跑腿上传图片地址(多张逗号隔开,最多五张)")
+    private String courierPics;
+
+    @ApiModelProperty("1待确认2进行中3已取消4已完成")
+    private Integer orderStatus;
+
+    @ApiModelProperty("订单编号")
+    private String orderNumber;
+
+    @ApiModelProperty("下单时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderTime;
+
+    @ApiModelProperty("支付方式 0-在线支付 1-会员支付")
+    private Integer payMethod;
+
+    @ApiModelProperty("支付金额")
+    private BigDecimal paymentAmount;
+
+    @ApiModelProperty("跑腿姓名")
+    private String courierName;
+
+    @ApiModelProperty("跑腿联系电话")
+    private String courierPhone;
+
+
+
+    @ApiModelProperty("评分(0.5-5.0,支持半星")
+    @TableField("rating")
+    private BigDecimal rating;
+
+    @ApiModelProperty("评价内容")
+    @TableField("content")
+    private String content;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderPageVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderPageVO.java
new file mode 100644
index 0000000..60b8c22
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderPageVO.java
@@ -0,0 +1,41 @@
+package com.ruoyi.errand.object.vo.app;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("下单页面相关信息")
+public class OrderPageVO {
+    @ApiModelProperty("小区id")
+    private Integer communityId;
+    @ApiModelProperty("小区名称")
+    private String communityName;
+    @ApiModelProperty("收费金额(元)")
+    private BigDecimal feeAmount;
+
+    @ApiModelProperty("地址簿id 没有则为null")
+    private Integer addressBookId;
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+
+    @ApiModelProperty("用户手机号")
+    private String phone;
+    @ApiModelProperty("会员id")
+    private Integer vipId;
+    @ApiModelProperty("第一次下单:0否1是")
+    private Integer isFirstOrder;
+    @ApiModelProperty("会员到期时间 ")
+    private LocalDateTime endTime;
+
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderTopInfoVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderTopInfoVO.java
new file mode 100644
index 0000000..2535870
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/OrderTopInfoVO.java
@@ -0,0 +1,39 @@
+package com.ruoyi.errand.object.vo.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("订单统计顶部信息")
+public class OrderTopInfoVO {
+    @ApiModelProperty("订单总数")
+    private Integer totalOrderNum=0;
+    @ApiModelProperty("订单总金额")
+    private BigDecimal totalOrderAmount=BigDecimal.ZERO.setScale(2,BigDecimal.ROUND_HALF_UP);
+
+    @ApiModelProperty("今日订单数")
+    private Integer todayOrderNum=0;
+    @ApiModelProperty("今日订单金额")
+    private BigDecimal todayOrderAmount=BigDecimal.ZERO.setScale(2,BigDecimal.ROUND_HALF_UP);
+
+    @ApiModelProperty("当月订单数")
+    private Integer mouthOrderNum=0;
+    @ApiModelProperty("当月订单金额")
+    private BigDecimal mouthOrderAmount=BigDecimal.ZERO.setScale(2,BigDecimal.ROUND_HALF_UP);
+
+
+    @ApiModelProperty("本季度订单数")
+    private Integer quarterOrderNum=0;
+    @ApiModelProperty("本季度订单金额")
+    private BigDecimal quarterOrderAmount=BigDecimal.ZERO.setScale(2,BigDecimal.ROUND_HALF_UP);
+
+
+    @ApiModelProperty("本年订单数")
+    private Integer yearOrderNum=0;
+    @ApiModelProperty("本年订单金额")
+    private BigDecimal yearOrderAmount=BigDecimal.ZERO.setScale(2,BigDecimal.ROUND_HALF_UP);
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/UserTopInfoVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/UserTopInfoVO.java
new file mode 100644
index 0000000..8abdeb4
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/UserTopInfoVO.java
@@ -0,0 +1,14 @@
+package com.ruoyi.errand.object.vo.app;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("用户统计-顶部数据")
+public class UserTopInfoVO {
+    @ApiModelProperty("总用户数")
+    private Integer totalUsers;
+    @ApiModelProperty("新增用户")
+    private Integer insertUsers;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/VipInfoListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/VipInfoListVO.java
new file mode 100644
index 0000000..444ca1d
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/app/VipInfoListVO.java
@@ -0,0 +1,22 @@
+package com.ruoyi.errand.object.vo.app;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("会员信息列表")
+public class VipInfoListVO {
+    @ApiModelProperty("1-4对应会员")
+    private Integer id;
+
+    @ApiModelProperty("会员名称")
+    private String vip_name;
+
+    @ApiModelProperty("会员价格")
+    private BigDecimal vip_price;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/login/LoginVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/login/LoginVO.java
new file mode 100644
index 0000000..18a6d11
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/login/LoginVO.java
@@ -0,0 +1,23 @@
+package com.ruoyi.errand.object.vo.login;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("登录结果")
+public class LoginVO {
+    @ApiModelProperty("token")
+    private String token;
+    @ApiModelProperty("失效时间(秒)")
+    private Long failureTime;
+    @ApiModelProperty("跳转页面(0=首页,1=注册页)")
+    private Integer skipPage;
+    @ApiModelProperty("微信解密的手机号")
+    private String phone;
+    @ApiModelProperty("是否是跑腿人员 true是 false不是")
+    private Boolean isCourier;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCommunityListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCommunityListVO.java
new file mode 100644
index 0000000..bb314aa
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCommunityListVO.java
@@ -0,0 +1,15 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("加载未绑定跑腿员的小区(后台)")
+public class AllCommunityListVO {
+    @ApiModelProperty("小区id")
+    private Integer communityId;
+
+    @ApiModelProperty("小区名称")
+    private String name;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCourierListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCourierListVO.java
new file mode 100644
index 0000000..4d2c1d2
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AllCourierListVO.java
@@ -0,0 +1,19 @@
+package com.ruoyi.errand.object.vo.sys;
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("加载未绑定小区的跑腿员")
+public class AllCourierListVO {
+
+    @ApiModelProperty("跑腿员ID")
+    private Integer courierId;
+    @ApiModelProperty("姓名")
+    private String name;
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserPageListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserPageListVO.java
new file mode 100644
index 0000000..21f981a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserPageListVO.java
@@ -0,0 +1,38 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("app用户管理分页(后台)")
+public class AppUserPageListVO {
+    @ApiModelProperty("主键")
+    private Long id;
+    @ApiModelProperty("用户")
+    private String name;
+    @ApiModelProperty("手机号")
+    private String phone;
+    @ApiModelProperty("是否会员 0否 1是")
+    private Integer isVip;
+
+    @ApiModelProperty("本月下单数")
+    private Integer mouth;
+
+    @ApiModelProperty("总计下单数")
+    private Integer total;
+
+    @ApiModelProperty("注册时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("状态(1=正常,2=冻结,3=注销)")
+    @TableField("status")
+    private Integer status;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserSysDetailVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserSysDetailVO.java
new file mode 100644
index 0000000..8d89135
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/AppUserSysDetailVO.java
@@ -0,0 +1,47 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("app用户详情(后台)")
+public class AppUserSysDetailVO {
+
+    @ApiModelProperty("用户")
+    private String name;
+
+    @ApiModelProperty("手机号")
+    private String phone;
+
+    @ApiModelProperty("是否会员 0否 1是")
+    private Integer isVip;
+
+    @ApiModelProperty("会员到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endTime;
+
+    @ApiModelProperty("性别(1=男,2=女,3=未知)")
+    private Integer sex;
+
+    @ApiModelProperty("生日")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate birthday;
+
+
+    @ApiModelProperty("添加时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("本月下单数")
+    private Integer mouth;
+
+    @ApiModelProperty("总计下单数")
+    private Integer total;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerDetailVo.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerDetailVo.java
new file mode 100644
index 0000000..558d28b
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerDetailVo.java
@@ -0,0 +1,23 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("banner查看详情VO")
+public class BannerDetailVo {
+    @TableId(type = IdType.AUTO)
+    @ApiModelProperty("主键")
+    private Integer id;
+    @ApiModelProperty("名称")
+    @TableField("name")
+    private String name;
+    @ApiModelProperty("图片地址")
+    @TableField("image_url")
+    private String imageUrl;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerPageListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerPageListVO.java
new file mode 100644
index 0000000..9a228f1
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/BannerPageListVO.java
@@ -0,0 +1,19 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("banner分页查询VO")
+public class BannerPageListVO {
+    @ApiModelProperty("主键")
+    private Integer id;
+
+    @ApiModelProperty("名称")
+    private String name;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunityPageListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunityPageListVO.java
new file mode 100644
index 0000000..6de5f42
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunityPageListVO.java
@@ -0,0 +1,35 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("小区管理分页VO")
+public class CommunityPageListVO {
+    @ApiModelProperty("小区id")
+    private Integer id;
+    @ApiModelProperty("小区名称")
+    private String name;
+    @ApiModelProperty("所在城市")
+    private String  regionName;
+    @ApiModelProperty("管理员名称")
+    private String adminName;
+    @ApiModelProperty("管理员联系电话")
+    private String adminPhone;
+    @ApiModelProperty("绑定跑腿员")
+    private String courierName;
+    @ApiModelProperty("订单总数")
+    private Integer total=0;
+    @ApiModelProperty("今日订单数")
+    private Integer today=0;
+
+    @ApiModelProperty("状态:0-正常,1-冻结")
+    @TableField("status")
+    private Integer status;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunitySysDetailVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunitySysDetailVO.java
new file mode 100644
index 0000000..28213c3
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CommunitySysDetailVO.java
@@ -0,0 +1,45 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("小区详情VO")
+public class CommunitySysDetailVO {
+    @ApiModelProperty("小区名称")
+    @TableField("name")
+    private String name;
+    @ApiModelProperty("小区详细地址")
+    private String address;
+
+    @ApiModelProperty("所在城市id")
+    private Integer regionId;
+    @ApiModelProperty("所在城市")
+    private String regionStr;
+
+    @ApiModelProperty("绑定跑腿员id")
+    private Integer courierId;
+    @ApiModelProperty("绑定跑腿员")
+    private String courierName;
+
+    @ApiModelProperty("管理员名称")
+    private String adminName;
+    @ApiModelProperty("管理员联系电话")
+    private String adminPhone;
+
+    @ApiModelProperty("客服电话")
+    private String servicePhone;
+
+    @ApiModelProperty("收费金额(元)")
+    private BigDecimal feeAmount;
+
+    @ApiModelProperty("今日订单数")
+    private Integer today;
+
+    @ApiModelProperty("订单总数")
+    private Integer total;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierPageListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierPageListVO.java
new file mode 100644
index 0000000..4e7e41b
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierPageListVO.java
@@ -0,0 +1,35 @@
+package com.ruoyi.errand.object.vo.sys;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("跑腿员管理分页")
+public class CourierPageListVO {
+    @ApiModelProperty("跑腿员ID")
+    private Integer id;
+
+    @ApiModelProperty("姓名")
+    private String name;
+
+    @ApiModelProperty("联系电话")
+    private String phone;
+
+    @ApiModelProperty("小区名称")
+    private String communityName;
+    @ApiModelProperty("当天接单量")
+    private Integer today=0;
+    @ApiModelProperty("本周接单量")
+    private Integer week=0;
+    @ApiModelProperty("本月接单量")
+    private Integer mouth=0;
+    @ApiModelProperty("五星好评")
+    private Integer five=0;
+    @ApiModelProperty("状态:0-离职,1-复职")
+    private Integer status=0;
+
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java
new file mode 100644
index 0000000..14a8247
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java
@@ -0,0 +1,44 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("跑腿人员详情(后台)")
+public class CourierSysDetailVO {
+
+    @ApiModelProperty("姓名")
+    private String name;
+
+    @ApiModelProperty("联系电话")
+    private String phone;
+
+    @ApiModelProperty("身份证号")
+    private String idCard;
+    @ApiModelProperty("小区id")
+    private String communityId;
+    @ApiModelProperty("小区名称")
+    private String communityName;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("当天接单量")
+    private Integer today=0;
+    @ApiModelProperty("本周接单量")
+    private Integer week=0;
+    @ApiModelProperty("本月接单量")
+    private Integer mouth=0;
+    @ApiModelProperty("五星好评")
+    private Integer five=0;
+    @ApiModelProperty("总共接单量")
+    private Integer total=0;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/EditBannerDTO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/EditBannerDTO.java
new file mode 100644
index 0000000..9bb7e1d
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/EditBannerDTO.java
@@ -0,0 +1,18 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.ruoyi.errand.object.dto.sys.AddBannerDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@ApiModel("编辑BannerDTO")
+public class EditBannerDTO extends AddBannerDTO {
+    @ApiModelProperty("主键")
+    private Integer id;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FeedbackPageListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FeedbackPageListVO.java
new file mode 100644
index 0000000..ee778a4
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FeedbackPageListVO.java
@@ -0,0 +1,30 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("用户反馈分页VO")
+public class FeedbackPageListVO {
+    @ApiModelProperty("反馈ID")
+    private Integer id;
+    @ApiModelProperty("反馈用户名称")
+    private String name;
+    @ApiModelProperty("联系电话")
+    private String phone;
+    @ApiModelProperty("反馈内容")
+    private String content;
+    @ApiModelProperty("反馈时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime create_time;
+    @ApiModelProperty("状态:0-未处理,1-已处理")
+    private Integer status;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java
new file mode 100644
index 0000000..4c3d894
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java
@@ -0,0 +1,41 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("财务统计列表VO")
+public class FinanceStatisticsVO {
+    @ApiModelProperty("订单id")
+    private Integer id;
+
+    @Excel(name = "下单时间",width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty("下单时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderTime;
+
+    @Excel(name = "类型", readConverterExp = "1=订单,2=会员充值")
+    @ApiModelProperty("类型 1-订单 2-会员充值")
+    private Integer type;
+
+    @Excel(name = "订单编号")
+    @ApiModelProperty("订单编号")
+    private String orderNumber;
+
+    @Excel(name = "用户名")
+    @ApiModelProperty("用户名")
+    private String appUserName;
+
+    @Excel(name = "联系电话")
+    @ApiModelProperty("联系电话")
+    private String phone;
+
+    @Excel(name = "订单状态", readConverterExp = "1=待确认,2=进行中,3=已取消,4=已完成,5=已评价,6=已退费")
+    @ApiModelProperty("订单状态 1待确认2进行中3已取消4已完成5已评价6已退费")
+    private Integer orderStatus;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java
new file mode 100644
index 0000000..fb170d7
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java
@@ -0,0 +1,63 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("订单管理分页")
+public class OrderPageListVO {
+
+    @ApiModelProperty("订单id")
+    private Integer id;
+
+    @Excel(name = "订单编号")
+    @ApiModelProperty("订单编号")
+    private String orderNumber;
+
+    @Excel(name = "下单时间",width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty("下单时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderTime;
+
+    @Excel(name = "支付金额")
+    @ApiModelProperty("支付金额")
+    @TableField("payment_amount")
+    private BigDecimal paymentAmount;
+
+    @Excel(name = "小区名称")
+    @ApiModelProperty("小区名称")
+    private String communityName;
+
+    @Excel(name = "下单用户")
+    @ApiModelProperty("下单用户")
+    private String appUserName;
+
+    @Excel(name = "联系电话")
+    @ApiModelProperty("联系电话")
+    private String appUserPhone;
+
+    @Excel(name = "跑腿员")
+    @ApiModelProperty("跑腿员")
+    private String courierName;
+
+    @Excel(name = "跑腿员联系电话")
+    @ApiModelProperty("跑腿员联系电话")
+    private String courierPhone;
+
+    @Excel(name = "完成时间",width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty("完成时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime finishTime;
+
+    @Excel(name = "订单状态", readConverterExp = "1=待确认,2=进行中,3=已取消,4=已完成,5=已评价")
+    @ApiModelProperty("订单状态 1待确认2进行中3已取消4已完成5已评价")
+    private Integer orderStatus;
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderSysDetailVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderSysDetailVO.java
new file mode 100644
index 0000000..f32a169
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderSysDetailVO.java
@@ -0,0 +1,81 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("订单详情(后台)")
+public class OrderSysDetailVO {
+    @ApiModelProperty("1待确认2进行中3已取消4已完成5已评价")
+    private Integer orderStatus;
+
+    @ApiModelProperty("下单用户")
+    private String appUserName;
+
+    @ApiModelProperty("联系电话")
+    private String appUserPhone;
+
+    @ApiModelProperty("所在小区")
+    private String communityName;
+
+    @ApiModelProperty("收件人姓名")
+    private String recipientName;
+
+    @ApiModelProperty("收件人电话")
+    private String recipientPhone;
+
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+
+    @ApiModelProperty("代办事项")
+    private String agencyMatters;
+
+    @ApiModelProperty("货品件数")
+    private Integer num;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @ApiModelProperty("上传图片地址(多张逗号隔开,最多五张)")
+    private String pics;
+
+    @ApiModelProperty("跑腿员")
+    private String courierName;
+
+    @ApiModelProperty("跑腿员联系电话")
+    private String courierPhone;
+
+    @ApiModelProperty("跑腿上传图片地址(多张逗号隔开,最多五张)")
+    private String courierPics;
+
+    @ApiModelProperty("订单编号")
+    private String orderNumber;
+
+    @ApiModelProperty("下单时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime orderTime;
+
+    @ApiModelProperty("完成时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime finishTime;
+
+    @ApiModelProperty("支付金额")
+    private BigDecimal paymentAmount;
+
+
+    @ApiModelProperty("用户评分(0.5-5.0,支持半星")
+    private BigDecimal userRating;
+    @ApiModelProperty("用户评价内容")
+    private String userContent;
+
+    @ApiModelProperty("跑腿员评分(0.5-5.0,支持半星")
+    private BigDecimal courierRating;
+    @ApiModelProperty("跑腿员评价内容")
+    private String courierContent;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/RefundVipOrderVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/RefundVipOrderVO.java
new file mode 100644
index 0000000..91e3e51
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/RefundVipOrderVO.java
@@ -0,0 +1,5 @@
+package com.ruoyi.errand.object.vo.sys;
+
+
+public class RefundVipOrderVO {
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/ReportPageListVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/ReportPageListVO.java
new file mode 100644
index 0000000..d0c4b35
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/ReportPageListVO.java
@@ -0,0 +1,40 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel
+public class ReportPageListVO {
+    @ApiModelProperty("上报id")
+    private Integer id;
+
+    @ApiModelProperty("小区名称")
+    private String communityName;
+
+    @ApiModelProperty("所在城市")
+    private String regionName;
+
+    @ApiModelProperty("详细地址")
+    private String addressDetail;
+
+    @ApiModelProperty("上报用户")
+    private String appUserName;
+
+    @ApiModelProperty("联系电话")
+    private String appUserPhone;
+
+    @ApiModelProperty("上报时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("状态 0-未处理1-已处理")
+    private Integer status;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/UserStatsVO.java b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/UserStatsVO.java
new file mode 100644
index 0000000..8e3fc03
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/UserStatsVO.java
@@ -0,0 +1,18 @@
+package com.ruoyi.errand.object.vo.sys;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@ApiModel("用户统计-折线图")
+public class UserStatsVO {
+
+    @ApiModelProperty("日期")
+    private List<String> date;
+    @ApiModelProperty("用户数")
+    private List<Integer> value;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/AddressBookService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/AddressBookService.java
new file mode 100644
index 0000000..efb0faf
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/AddressBookService.java
@@ -0,0 +1,24 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.AddressBook;
+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 java.util.List;
+
+public interface AddressBookService extends IService<AddressBook> {
+    List<AddressBookByCommunityIdVO> addressBookByCommunityId(Integer communityId);
+
+    List<AddressBookListVO> addressBookList();
+
+    void setDefaultAddress(Integer id);
+
+    void add(AddAddressBookDTO addAddressBookDTO);
+
+    void set(UpdateAddressBookDTO updateAddressBookDTO);
+
+    void delete(Integer id);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/AgreementService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/AgreementService.java
new file mode 100644
index 0000000..f995223
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/AgreementService.java
@@ -0,0 +1,8 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.Agreement;
+
+public interface AgreementService extends IService<Agreement> {
+    Agreement getAgreementByType(Integer type);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/AppUserService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/AppUserService.java
new file mode 100644
index 0000000..41bd3f7
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/AppUserService.java
@@ -0,0 +1,58 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.errand.domain.AppUser;
+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.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.errand.object.vo.sys.AppUserPageListVO;
+import com.ruoyi.errand.object.vo.sys.AppUserSysDetailVO;
+import com.ruoyi.errand.object.vo.sys.UserStatsVO;
+import com.ruoyi.errand.utils.RefundCallbackResult;
+
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+
+public interface AppUserService extends IService<AppUser> {
+
+
+    void getSMSCode(String phone);
+
+    R<LoginVO> mobileLogin( MobileLoginDTO mobileLogin);
+
+    R<LoginVO> appletLogin( AppletLogin appletLogin);
+
+    void register(RegisterDTO registerDTO);
+
+    OrderPageVO getOrderPage( Integer communityId);
+
+    AppUserInfoVO getMyInfo();
+
+    void setSex(Integer sex);
+
+    void setBirthDay(BirthDayDTO birth);
+
+    void delete();
+
+    UserStatsVO getUserStats(LocalDateTime start, LocalDateTime end, String datePattern);
+
+    UserTopInfoVO userTopInfo(LocalDateTime start, LocalDateTime end);
+
+    IPage<AppUserPageListVO> getAppUserPageList( AppUserPageListDTO appUserPageListDTO);
+
+    AppUserSysDetailVO detail(Integer id);
+
+    void froze(Integer id);
+
+    void refund(Integer id);
+
+    R refundPayMoneyCallback(RefundCallbackResult refundCallbackResult);
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/BannerService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/BannerService.java
new file mode 100644
index 0000000..34d04a2
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/BannerService.java
@@ -0,0 +1,27 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.Banner;
+import com.ruoyi.errand.object.dto.sys.AddBannerDTO;
+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 java.util.List;
+
+
+public interface BannerService extends IService<Banner> {
+    List<BannerVO> getBannerList();
+
+    void add(AddBannerDTO addBannerDTO);
+
+    IPage<BannerPageListVO> pageList(IPage<BannerPageListVO> iPage, String name);
+
+    void edit(EditBannerDTO editBannerDTO);
+
+    void delete(Integer id);
+
+    BannerDetailVo detail(Integer id);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/CommunityCourierService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/CommunityCourierService.java
new file mode 100644
index 0000000..97ef64a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/CommunityCourierService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.CommunityCourier;
+
+public interface CommunityCourierService extends IService<CommunityCourier> {
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/CommunityService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/CommunityService.java
new file mode 100644
index 0000000..40b17af
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/CommunityService.java
@@ -0,0 +1,36 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.errand.domain.Community;
+import com.ruoyi.errand.object.dto.sys.AddCommunityDTO;
+import com.ruoyi.errand.object.dto.sys.CommunityPageListDTO;
+import com.ruoyi.errand.object.dto.sys.EditCommunityDTO;
+import com.ruoyi.errand.object.vo.app.CommunityListVO;
+import com.ruoyi.errand.object.vo.sys.AllCommunityListVO;
+import com.ruoyi.errand.object.vo.sys.CommunityPageListVO;
+import com.ruoyi.errand.object.vo.sys.CommunitySysDetailVO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+public interface CommunityService extends IService<Community> {
+    R<List<CommunityListVO>> getCommunity(Integer communityId);
+
+    R<List<CommunityListVO>> getTotalCommunityList();
+
+    List<AllCommunityListVO> getAllCommunityList();
+
+    IPage<CommunityPageListVO> getCommunityPageList( CommunityPageListDTO communityPageListDTO);
+
+    void add( AddCommunityDTO addCommunityDTO);
+
+    void edit( EditCommunityDTO editCommunityDTO);
+
+    void delete(Integer id);
+
+    void froze(Integer id);
+
+    CommunitySysDetailVO detail(Integer id);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/CourierService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/CourierService.java
new file mode 100644
index 0000000..2f1262f
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/CourierService.java
@@ -0,0 +1,45 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.errand.domain.Courier;
+import com.ruoyi.errand.object.dto.sys.AddCourierDTO;
+import com.ruoyi.errand.object.dto.sys.CourierPageListDTO;
+import com.ruoyi.errand.object.dto.sys.EditCourierDTO;
+import com.ruoyi.errand.object.vo.app.CompleteOrderDTO;
+import com.ruoyi.errand.object.vo.app.CourierInfoVO;
+import com.ruoyi.errand.object.vo.app.CourierOrderListVO;
+import com.ruoyi.errand.object.vo.app.CourierStatisticsVO;
+import com.ruoyi.errand.object.vo.sys.AllCourierListVO;
+import com.ruoyi.errand.object.vo.sys.CourierPageListVO;
+import com.ruoyi.errand.object.vo.sys.CourierSysDetailVO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+public interface CourierService extends IService<Courier> {
+    CourierInfoVO getCourierInfo();
+
+    CourierStatisticsVO getDatStatistics();
+
+    IPage<CourierOrderListVO> getCourierOrderList(Integer pageNum, Integer pageSize, Integer orderStatus);
+
+    void receiveOrder(Integer id);
+
+    void completeOrder( CompleteOrderDTO completeOrderDTO);
+
+    IPage<CourierPageListVO> getCourierPageList( CourierPageListDTO courierPageListDTO);
+
+    CourierSysDetailVO detail(Integer id);
+
+    void add( AddCourierDTO addCourierDTO);
+
+    void edit( EditCourierDTO editCourierDTO);
+
+    void froze(Integer id);
+
+    void delete(Integer id);
+
+    List<AllCourierListVO> getAllCourierList();
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/EvaluationService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/EvaluationService.java
new file mode 100644
index 0000000..90d9528
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/EvaluationService.java
@@ -0,0 +1,9 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.Evaluation;
+import com.ruoyi.errand.object.dto.app.AddEvaluationDTO;
+
+public interface EvaluationService extends IService<Evaluation> {
+    void add( AddEvaluationDTO addEvaluationDTO);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/FeedbackService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/FeedbackService.java
new file mode 100644
index 0000000..23693ad
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/FeedbackService.java
@@ -0,0 +1,19 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.Feedback;
+import com.ruoyi.errand.object.dto.sys.FeedbackPageListDTO;
+import com.ruoyi.errand.object.vo.sys.FeedbackPageListVO;
+
+import javax.validation.Valid;
+
+public interface FeedbackService extends IService<Feedback> {
+    void add(String content);
+
+    IPage<FeedbackPageListVO> getFeedbackPageList(FeedbackPageListDTO feedbackPageListDTO);
+
+    void delete(Integer id);
+
+    void dispose(Integer id);
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/OrderService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/OrderService.java
new file mode 100644
index 0000000..f9e0894
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/OrderService.java
@@ -0,0 +1,59 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.errand.domain.Order;
+import com.ruoyi.errand.object.dto.app.ConfirmOrderDTO;
+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.vo.app.AppUserOrderListVO;
+import com.ruoyi.errand.object.vo.app.ConfirmOrderVO;
+import com.ruoyi.errand.object.vo.app.OrderDetailVO;
+import com.ruoyi.errand.object.vo.app.OrderTopInfoVO;
+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.utils.RefundCallbackResult;
+import com.ruoyi.errand.utils.UniPayCallbackResult;
+
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+import java.util.List;
+
+public interface OrderService extends IService<Order> {
+    ConfirmOrderVO confirmOrder( ConfirmOrderDTO confirmOrderDTO);
+
+
+    R orderPayment(ConfirmOrderDTO confirmOrderDTO);
+
+    R orderPaymentCallback(UniPayCallbackResult uniPayCallbackResult);
+
+    void closeOrder();
+
+    IPage<AppUserOrderListVO> getAppUserOrderList(Integer pageNum, Integer pageSize, Integer orderStatus);
+
+    OrderDetailVO getOrderDetail(Integer id);
+
+    void setOrderInfo( SetConfirmOrderDTO setConfirmOrderDTO);
+
+    void cancelOrder(Integer id);
+
+    R refundPayMoneyCallback(RefundCallbackResult refundCallbackResult);
+
+    OrderTopInfoVO orderTopInfo(Integer communityId);
+
+    OrderStatsVO getOrderStats(LocalDateTime start, LocalDateTime end, String datePattern, Integer communityId);
+
+    IPage<FinanceStatisticsVO> financeStatistics( FinanceStatisticsDTO financeStatisticsDTO);
+
+    IPage<OrderPageListVO> getOrderPageList( OrderPageListDTO orderPageListDTO);
+
+    OrderSysDetailVO detail(Integer id);
+
+    List<FinanceStatisticsVO> export( FinanceStatisticsDTO financeStatisticsDTO);
+
+    List<OrderPageListVO> orderExport( OrderPageListDTO orderPageListDTO);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/PhoneService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/PhoneService.java
new file mode 100644
index 0000000..7d4d305
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/PhoneService.java
@@ -0,0 +1,10 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.Phone;
+
+public interface PhoneService extends IService<Phone> {
+    String getServletPhone();
+
+    void saveServicePhone(String phone);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/RegionService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/RegionService.java
new file mode 100644
index 0000000..e9f2ad3
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/RegionService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.Region;
+
+public interface RegionService extends IService<Region> {
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/ReportService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/ReportService.java
new file mode 100644
index 0000000..7ae402f
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/ReportService.java
@@ -0,0 +1,20 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.Report;
+import com.ruoyi.errand.object.dto.app.AddReportDTO;
+import com.ruoyi.errand.object.dto.sys.ReportPageListDTO;
+import com.ruoyi.errand.object.vo.sys.ReportPageListVO;
+
+import javax.validation.Valid;
+
+public interface ReportService extends IService<Report> {
+    void add(AddReportDTO addReportDTO);
+
+    IPage<ReportPageListVO> getReportList(ReportPageListDTO reportPageListDTO);
+
+    void dispose(Integer id);
+
+    void delete(Integer id);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/SystemConfigService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/SystemConfigService.java
new file mode 100644
index 0000000..a6e18da
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/SystemConfigService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.SystemConfig;
+
+public interface SystemConfigService extends IService<SystemConfig> {
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/UserCancellationLogService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/UserCancellationLogService.java
new file mode 100644
index 0000000..7735de0
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/UserCancellationLogService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.UserCancellationLog;
+
+public interface UserCancellationLogService extends IService<UserCancellationLog> {
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/VipOrderService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/VipOrderService.java
new file mode 100644
index 0000000..f5642a1
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/VipOrderService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.errand.domain.VipOrder;
+import com.ruoyi.errand.object.dto.app.VipPaymentDTO;
+import com.ruoyi.errand.utils.UniPayCallbackResult;
+
+
+public interface VipOrderService extends IService<VipOrder> {
+    R vipPayment(VipPaymentDTO vipPaymentDTO);
+
+    R orderPaymentCallback(UniPayCallbackResult uniPayCallbackResult);
+
+    void closeOrder();
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/VipSettingService.java b/pt-errand/src/main/java/com/ruoyi/errand/service/VipSettingService.java
new file mode 100644
index 0000000..515d412
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/VipSettingService.java
@@ -0,0 +1,15 @@
+package com.ruoyi.errand.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.errand.domain.VipSetting;
+import com.ruoyi.errand.object.dto.sys.SetPriceDTO;
+import com.ruoyi.errand.object.vo.app.VipInfoListVO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+public interface VipSettingService extends IService<VipSetting> {
+    List<VipInfoListVO> getVipInfoList();
+
+    void setPrice( SetPriceDTO setPriceDTO);
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AddressBookServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AddressBookServiceImpl.java
new file mode 100644
index 0000000..8e730c5
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AddressBookServiceImpl.java
@@ -0,0 +1,130 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.constant.IsDefaultConstant;
+import com.ruoyi.errand.domain.AddressBook;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.domain.Community;
+import com.ruoyi.errand.mapper.AddressBookMapper;
+import com.ruoyi.errand.mapper.CommunityMapper;
+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 org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Service
+public class AddressBookServiceImpl extends ServiceImpl<AddressBookMapper, AddressBook> implements AddressBookService {
+    @Resource
+    private CommunityMapper communityMapper;
+
+    @Override
+    public List<AddressBookByCommunityIdVO> addressBookByCommunityId(Integer communityId) {
+        //校验一下communityId
+        Community community = communityMapper.selectById(communityId);
+        if (community == null|| community.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("小区不存在");
+        }
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        return  this.getBaseMapper().addressBookByCommunityId(communityId,appuser.getId());
+    }
+
+    @Override
+    public List<AddressBookListVO> addressBookList() {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        return this.getBaseMapper().addressBookList(appuser.getId());
+    }
+
+    @Override
+    public void setDefaultAddress(Integer id) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //查询这个默认地址
+        AddressBook addressBook = this.getById(id);
+        if (null==addressBook||addressBook.getDel_flag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("该地址不存在");
+        }
+        //查看原本小区内是否有默认地址
+        List<AddressBook> addressBooks = this.getBaseMapper().selectList(new LambdaUpdateWrapper<AddressBook>()
+                .eq(AddressBook::getCommunityId, addressBook.getCommunityId())
+                .eq(AddressBook::getApp_user_id, appuser.getId())
+                .eq(AddressBook::getDel_flag, DelFlagConstant.UNDELETE)
+                .eq(AddressBook::getIs_default, IsDefaultConstant.TRUE)
+        );
+        //将原本地址修改掉
+        addressBooks.forEach(x->{
+            x.setIs_default(IsDefaultConstant.FALSE);
+            x.setUpdate_time(LocalDateTime.now());
+            this.getBaseMapper().updateById(x);}
+        );
+        //将新地址设置为默认地址
+        addressBook.setIs_default(IsDefaultConstant.TRUE);
+        addressBook.setUpdate_time(LocalDateTime.now());
+        this.getBaseMapper().updateById(addressBook);
+    }
+
+    @Override
+    public void add(AddAddressBookDTO addAddressBookDTO) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //判断小区是否存在
+        Community community = communityMapper.selectById(addAddressBookDTO.getCommunityId());
+        if (null==community|| community.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("小区不存在");
+        }
+        AddressBook addressBook = new AddressBook();
+        BeanUtils.copyProperties(addAddressBookDTO,addressBook);
+        addressBook.setIs_default(IsDefaultConstant.FALSE);
+        addressBook.setCreate_time(LocalDateTime.now());
+        addressBook.setApp_user_id(appuser.getId());
+        this.save(addressBook);
+    }
+
+    @Override
+    public void set(UpdateAddressBookDTO updateAddressBookDTO) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //判断小区是否存在
+        Community community = communityMapper.selectById(updateAddressBookDTO.getCommunityId());
+        if (null==community|| community.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("小区不存在");
+        }
+        //判断地址是否存在
+        AddressBook addressBook = this.getById(updateAddressBookDTO.getId());
+        if (null==addressBook||addressBook.getDel_flag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("地址不存在");
+        }
+        //判断用户是否同一个
+        if (!appuser.getId().equals(addressBook.getApp_user_id())) {
+            throw new ServiceException("该地址不属于您");
+        }
+        BeanUtils.copyProperties(updateAddressBookDTO,addressBook);
+        addressBook.setUpdate_time(LocalDateTime.now());
+        this.updateById(addressBook);
+
+    }
+
+    @Override
+    public void delete(Integer id) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //查看该地址是否存在
+        AddressBook addressBook = this.getById(id);
+        if (null==addressBook||addressBook.getDel_flag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("该地址不存在");
+        }
+        //判断用户是否同一个
+        if (!appuser.getId().equals(addressBook.getApp_user_id())) {
+            throw new ServiceException("该地址不属于您");
+        }
+
+        addressBook.setDel_flag(DelFlagConstant.DELETE);
+        this.updateById(addressBook);
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AgreementServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AgreementServiceImpl.java
new file mode 100644
index 0000000..2efe773
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AgreementServiceImpl.java
@@ -0,0 +1,17 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.errand.domain.Agreement;
+import com.ruoyi.errand.mapper.AgreementMapper;
+import com.ruoyi.errand.service.AgreementService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AgreementServiceImpl extends ServiceImpl<AgreementMapper, Agreement> implements AgreementService {
+
+    @Override
+    public Agreement getAgreementByType(Integer type) {
+        return  this.getBaseMapper().selectOne(new LambdaQueryWrapper<Agreement>().eq(Agreement::getType, type));
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java
new file mode 100644
index 0000000..98b186f
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java
@@ -0,0 +1,557 @@
+package com.ruoyi.errand.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.errand.constant.*;
+import com.ruoyi.errand.domain.AppUser;
+
+import com.ruoyi.errand.domain.Order;
+import com.ruoyi.errand.domain.UserCancellationLog;
+import com.ruoyi.errand.domain.VipOrder;
+import com.ruoyi.errand.mapper.AppUserMapper;
+
+import com.ruoyi.errand.mapper.OrderMapper;
+import com.ruoyi.errand.mapper.UserCancellationLogMapper;
+import com.ruoyi.errand.mapper.VipOrderMapper;
+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.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.errand.object.vo.sys.AppUserPageListVO;
+import com.ruoyi.errand.object.vo.sys.AppUserSysDetailVO;
+import com.ruoyi.errand.object.vo.sys.OrderPageListVO;
+import com.ruoyi.errand.object.vo.sys.UserStatsVO;
+import com.ruoyi.errand.service.AppUserService;
+import com.ruoyi.errand.utils.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AppUserService {
+    @Resource
+    private RedisService redisService;
+
+    @Resource
+    private WeChatUtil weChatUtil;
+    private static final String DEFAULT_AVATAR_URL = "http://qijishenghuiyuan.obs.cn-southwest-2.myhuaweicloud.com/admin/aedfbbb41280471f8d9fa7905298b65f.png";
+    @Autowired
+    private UserCancellationLogMapper userCancellationLogMapper;
+    @Autowired
+    private OrderMapper orderMapper;
+    @Autowired
+    private VipOrderMapper vipOrderMapper;
+
+
+    @Override
+    public void getSMSCode(String phone) {
+        //校验验证码获取频率(1分钟5次)
+        String key = "app" + "&" + phone;
+        Map<String, Object> cacheMap = redisService.getCacheMap(key);
+        if(null != cacheMap && cacheMap.size() > 0){
+            Integer number = Integer.valueOf(cacheMap.get("number").toString()) + 1;
+            Long startTime = Long.valueOf(cacheMap.get("startTime").toString());
+            if(number > 5 && (System.currentTimeMillis() - startTime) < 60000){
+                throw new ServiceException("获取验证码太频繁,请稍后重试!");
+            }
+            if(number <= 5){
+                cacheMap.put("number", number);
+            }else{
+                cacheMap.put("number", 1);
+                cacheMap.put("startTime", System.currentTimeMillis());
+            }
+        }else{
+            cacheMap = new HashMap<>();
+            cacheMap.put("number", 1);
+            cacheMap.put("startTime", System.currentTimeMillis());
+        }
+        //存储计数器到缓存中,5分钟有效期
+        redisService.setCacheMap(key, cacheMap, 300);
+
+        //开始构建验证码内容
+        String code = "";
+        for (int i = 0; i < 6; i++) {
+            code += Double.valueOf(Math.random() * 10).intValue();
+        }
+        SMSUtil.sendSms("[\"" + code + "\"]", phone, "8824121211029", "39533d100b2b4aee8ed198aa49fe99dd");
+        redisService.setCacheObject(phone, code, 300L, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public R<LoginVO> mobileLogin(MobileLoginDTO mobileLogin) {
+        String code = redisService.getCacheObject(mobileLogin.getPhone());
+        if(!"999999".equals(mobileLogin.getCode())){
+            if(null == code || !code.equals(mobileLogin.getCode())){
+                throw new ServiceException("验证码错误");
+            }
+        }
+        //查看用户是否存在
+        AppUser appUser = this.getOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, mobileLogin.getPhone()));
+        if (null == appUser || appUser.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            //用户不存在
+            //使用jscode获取微信openid
+            Map<String, Object> map = weChatUtil.code2Session(mobileLogin.getJscode());
+            Integer errcode = Integer.valueOf(map.get("errcode").toString());
+            if(0 != errcode){
+                throw new ServiceException(map.get("msg").toString());
+            }
+            String openid = map.get("openid").toString();
+            //注册一个
+            appUser = new AppUser();
+            appUser.setPhone(mobileLogin.getPhone());
+            appUser.setDelFlag(DelFlagConstant.UNDELETE);
+            appUser.setStatus(AppUserStatusConstant.NORMAL);
+            appUser.setWxOpenid(openid);
+            appUser.setAvatar(DEFAULT_AVATAR_URL);
+            appUser.setFirstLogin(IsFirstLoginConstant.YES);
+            appUser.setFirstOrder(IsFirstOrder.YES);
+            this.save(appUser);
+        }
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.FREEZE)) {
+            throw new ServiceException("该账户已被冻结");
+        }
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.LOGOUT)) {
+            throw new ServiceException("该账户已被注销");
+        }
+        appUser.setLastLoginTime(LocalDateTime.now());
+        this.updateById(appUser);
+        //构建token
+        Map<String,Object> map = new HashMap<>();
+        map.put("userId",appUser.getId());
+        Map<String, Object> jwt = JwtUtil.createJWT(map);
+
+
+        LoginVO loginVO=new LoginVO();
+        loginVO.setToken("app:" + jwt.get("token").toString());
+        loginVO.setFailureTime(TimeUnit.MILLISECONDS.toSeconds((long)jwt.get("exp")));
+        loginVO.setPhone(appUser.getPhone());
+        loginVO.setSkipPage(appUser.getFirstLogin());
+        loginVO.setIsCourier(appUser.getCourierId()!=null);
+        return R.ok(loginVO);
+    }
+
+    @Override
+    public R<LoginVO> appletLogin(AppletLogin appletLogin) {
+        //使用jscode获取微信openid
+        Map<String, Object> map = weChatUtil.code2Session(appletLogin.getJscode());
+        Integer errcode = Integer.valueOf(map.get("errcode").toString());
+        if(0 != errcode){
+            throw new ServiceException(map.get("msg").toString());
+        }
+        String openid = map.get("openid").toString();
+        String sessionKey = map.get("sessionKey").toString();
+        //查询用户是否注册,没有注册则注册
+        AppUser appUser = this.getOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getWxOpenid, openid));
+        if(null == appUser|| appUser.getDelFlag().equals(DelFlagConstant.DELETE)){
+            appUser = new AppUser();
+            //注册
+            //获取手机号
+            String decrypt = WXCore.decrypt(appletLogin.getEncryptedData_phone(), sessionKey, appletLogin.getIv_phone());
+            if (StringUtils.isEmpty(decrypt)) {
+                return R.fail("获取手机信息失败");
+            }
+            JSONObject phone = JSON.parseObject(decrypt);
+            String purePhoneNumber = phone.getString("phoneNumber");
+            //新用户默认信息
+            appUser = new AppUser();
+            appUser.setPhone(purePhoneNumber);
+            appUser.setDelFlag(DelFlagConstant.UNDELETE);
+            appUser.setStatus(AppUserStatusConstant.NORMAL);
+            appUser.setWxOpenid(openid);
+            appUser.setAvatar(DEFAULT_AVATAR_URL);
+            appUser.setFirstLogin(IsFirstLoginConstant.YES);
+            appUser.setFirstOrder(IsFirstOrder.YES);
+            this.save(appUser);
+
+        }
+
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.FREEZE)) {
+            throw new ServiceException("该账户已被冻结");
+        }
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.LOGOUT)) {
+            throw new ServiceException("该账户已被注销");
+        }
+        //构建token
+        Map<String,Object> tokenMap = new HashMap<>();
+        map.put("userId",appUser.getId());
+        Map<String, Object> jwt = JwtUtil.createJWT(tokenMap);
+
+
+        LoginVO loginVO=new LoginVO();
+        loginVO.setToken("app:" + jwt.get("token").toString());
+        loginVO.setFailureTime(TimeUnit.MILLISECONDS.toSeconds((long)jwt.get("exp")));
+        loginVO.setPhone(appUser.getPhone());
+        loginVO.setSkipPage(appUser.getFirstLogin());
+        return R.ok(loginVO);
+    }
+
+    @Override
+    public void register(RegisterDTO registerDTO) {
+        //注册 修改用户信息
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        appUser.setName(registerDTO.getUsername());
+        appUser.setFirstLogin(IsFirstLoginConstant.NO);
+        appUser.setCommunityId(registerDTO.getCommunityId());
+        this.updateById(appUser);
+
+    }
+
+    @Override
+    public OrderPageVO getOrderPage(Integer communityId) {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //更换绑定的小区
+        if (communityId!=null){
+            appUser.setCommunityId(communityId);
+            this.updateById(appUser);
+        }
+        List<OrderPageVO> orderPage = this.getBaseMapper().getOrderPage(appUser.getId());
+
+        return orderPage.get(0);
+    }
+
+    @Override
+    public AppUserInfoVO getMyInfo() {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        return this.getBaseMapper().getMyInfo(appUser.getId());
+    }
+
+    @Override
+    public void setSex(Integer sex) {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        if (appUser.getSex().equals(sex)) {
+            throw new ServiceException("修改的性别与当前性别重复");
+        }
+        appUser.setSex(sex);
+
+        updateById(appUser);
+
+    }
+
+    @Override
+    public void setBirthDay(BirthDayDTO birth) {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        appUser.setBirthday(birth.getBirthday());
+        updateById(appUser);
+    }
+
+    /**
+     * 注销账号
+     */
+    @Override
+    public void delete() {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        appuser.setDelFlag(DelFlagConstant.DELETE);
+        appuser.setStatus(AppUserStatusConstant.LOGOUT);
+        this.updateById(appuser);
+        UserCancellationLog userCancellationLog=new UserCancellationLog();
+        userCancellationLog.setApp_user_id(appuser.getId());
+        userCancellationLog.setDel_flag(DelFlagConstant.DELETE);
+        userCancellationLog.setCreate_time(LocalDateTime.now());
+        userCancellationLogMapper.insert(userCancellationLog);
+    }
+
+    @Override
+    public UserStatsVO getUserStats(LocalDateTime start, LocalDateTime end, String datePattern) {
+        UserStatsVO vo = new UserStatsVO();
+        // 1. 查询按时间分组的用户数
+        List<Map<String, Object>> stats = this.getBaseMapper().countGroupByDate(start, end, datePattern);
+
+        // 2. 生成完整的日期序列并填充数据
+        fillDateAndValue(vo, start, end, datePattern, stats);
+
+        return vo;
+    }
+
+    @Override
+    public UserTopInfoVO userTopInfo(LocalDateTime start, LocalDateTime end) {
+        UserTopInfoVO userTopInfoVO = new UserTopInfoVO();
+        // 1. 查询总用户数
+        userTopInfoVO.setTotalUsers(this.getBaseMapper().countByCreateTimeBefore(end));
+
+        // 2. 查询新增用户数
+        userTopInfoVO.setInsertUsers(this.getBaseMapper().countByCreateTimeBetween(start, end));
+        return userTopInfoVO;
+    }
+
+    @Override
+    public IPage<AppUserPageListVO> getAppUserPageList(AppUserPageListDTO dto) {
+        // 获取本月的开始时间和结束时间
+        LocalDate now = LocalDate.now();
+        LocalDateTime startOfMonth = now.withDayOfMonth(1).atStartOfDay();
+        LocalDateTime endOfMonth = now.withDayOfMonth(now.lengthOfMonth()).atTime(23, 59, 59);
+
+        IPage<AppUserPageListVO> page = new Page<>(dto.getPageNum(),dto.getPageSize());
+        IPage<AppUserPageListVO> iPage = this.getBaseMapper().getAppUserPageList(page, dto);
+        iPage.getRecords().forEach((record) -> {
+           //当月下单量
+            Long mouth = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                    .eq(Order::getAppUserId, record.getId())
+                    .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+                    .between(Order::getOrderTime, startOfMonth, endOfMonth)
+            );
+            record.setMouth(mouth.intValue());
+
+            //总共下单量
+            Long total = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                    .eq(Order::getAppUserId, record.getId())
+                    .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+            );
+            record.setTotal(total.intValue());
+        });
+        return iPage;
+    }
+
+    @Override
+    public AppUserSysDetailVO detail(Integer id) {
+        // 获取本月的开始时间和结束时间
+        LocalDate now = LocalDate.now();
+        LocalDateTime startOfMonth = now.withDayOfMonth(1).atStartOfDay();
+        LocalDateTime endOfMonth = now.withDayOfMonth(now.lengthOfMonth()).atTime(23, 59, 59);
+        AppUser appUser = this.getBaseMapper().selectById(id);
+        AppUserSysDetailVO vo = new AppUserSysDetailVO();
+        BeanUtils.copyProperties(appUser, vo);
+        int isVip =0;
+        if (appUser.getEndTime() != null) {
+            isVip = appUser.getEndTime().isAfter(LocalDateTime.now())?1:0;
+        }
+        vo.setIsVip(isVip);
+        //当月下单量
+        Long mouth = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                .eq(Order::getAppUserId, id)
+                .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+                .between(Order::getOrderTime, startOfMonth, endOfMonth)
+        );
+        vo.setMouth(mouth.intValue());
+
+        //总共下单量
+        Long total = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                .eq(Order::getAppUserId,id)
+                .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+        );
+        vo.setTotal(total.intValue());
+        return vo;
+    }
+
+    @Override
+    public void froze(Integer id) {
+        AppUser appUser = this.getBaseMapper().selectById(id);
+        if (appUser==null|| Objects.equals(appUser.getDelFlag(), DelFlagConstant.DELETE)) {
+            throw new ServiceException("用户不存在");
+        }
+
+        if (appUser.getStatus()==1 || appUser.getStatus()==2) {
+            appUser.setStatus(appUser.getStatus()==1?2:1);
+        }else {
+            throw new ServiceException("用户状态错误");
+        }
+
+        this.getBaseMapper().updateById(appUser);
+
+    }
+
+    @Override
+    public void refund(Integer id) {
+        // 1. 查询用户所有未退费的VIP订单(状态为4-已完成且未删除)
+        List<VipOrder> orders = vipOrderMapper.selectList(new LambdaQueryWrapper<VipOrder>()
+                .eq(VipOrder::getAppUserId, id)
+                .eq(VipOrder::getOrderStatus, 4) // 4-已完成
+                .eq(VipOrder::getDelFlag, DelFlagConstant.UNDELETE)     // 0-未删除
+                .isNull(VipOrder::getRefundTime) // 未退款的
+                .orderByDesc(VipOrder::getOrderTime));
+
+        // 2. 计算每个订单的有效期结束时间
+        Map<Long, LocalDateTime> orderEndTimes = calculateOrderEndTimes(orders);
+
+        // 3. 筛选出仍在有效期内的订单
+        LocalDateTime now = LocalDateTime.now();
+        List<VipOrder> refundVipOrderList = orders.stream()
+                .filter(order -> {
+                    LocalDateTime endTime = orderEndTimes.get(order.getId());
+                    return endTime != null && endTime.isAfter(now);
+                })
+                .collect(Collectors.toList());
+        if(refundVipOrderList.isEmpty()){
+            return;
+        }
+        // 4.对每个订单进行退款操作
+        refundVipOrderList.forEach(vipOrder -> {
+            //退款
+            log.info("开始会员退费,被退款人员id:{},退款订单:{}",vipOrder.getAppUserId(),vipOrder.getId());
+            R r = refundPayMoney(vipOrder);//退款
+            if (200 == r.getCode()) {
+                //退款成功取消用户的会员,将用户状态改变
+                AppUser appUser = this.getById(id);
+                appUser.setStartTime(LocalDateTime.now());
+                appUser.setEndTime(LocalDateTime.now());
+                appUser.setVipId(null);
+            }
+        });
+    }
+
+    @Override
+    public R refundPayMoneyCallback(RefundCallbackResult refundCallbackResult) {
+        String code = refundCallbackResult.getR3_RefundOrderNo().substring(1);
+        VipOrder vipOrder = vipOrderMapper.selectOne(new LambdaQueryWrapper<VipOrder>().eq(VipOrder::getOrderNumber, code));
+        if (null == vipOrder || vipOrder.getPayStatus() == 1 || vipOrder.getOrderStatus() == 6) {
+            return R.ok();
+        }
+        vipOrder.setRefundCode(refundCallbackResult.getR5_RefundTrxNo());
+        vipOrder.setRefundStatus(2);
+        vipOrder.setRefundTime(LocalDateTime.now());
+        vipOrder.setOrderStatus(6);//已退费
+        vipOrderMapper.updateById(vipOrder);
+        return R.ok();
+    }
+
+    private Map<Long, LocalDateTime> calculateOrderEndTimes(List<VipOrder> orders) {
+        Map<Long, LocalDateTime> result = new HashMap<>();
+        LocalDateTime currentEndTime = null;
+
+        // 按购买时间升序处理,计算叠加后的有效期
+        List<VipOrder> sortedOrders = orders.stream()
+                .sorted(Comparator.comparing(VipOrder::getOrderTime))
+                .collect(Collectors.toList());
+
+        for (VipOrder order : sortedOrders) {
+            int daysToAdd = getVipDays(order.getVipId());
+            if (daysToAdd == 0) continue;
+
+            LocalDateTime orderTime = order.getOrderTime();
+            LocalDateTime orderEndTime = orderTime.plusDays(daysToAdd)
+                    .withHour(23).withMinute(59).withSecond(59);
+
+            // 如果当前订单购买时间在上一个有效期之后,则重新计算
+            if (currentEndTime == null || orderTime.isAfter(currentEndTime)) {
+                currentEndTime = orderEndTime;
+            } else {
+                // 否则叠加有效期
+                currentEndTime = currentEndTime.plusDays(daysToAdd)
+                        .withHour(23).withMinute(59).withSecond(59);
+            }
+
+            result.put(order.getId(), currentEndTime);
+        }
+
+        return result;
+    }
+
+    private int getVipDays(Integer vipId) {
+        switch (vipId) {
+            case 1: return 31;
+            case 2: return 90;
+            case 3: return 180;
+            case 4: return 365;
+            default: return 0;
+        }
+    }
+    /**
+     * 返回订单支付金额
+     */
+    public R refundPayMoney(VipOrder order) {
+        //开始退款
+        BigDecimal paymentAmount = order.getPaymentAmount();
+        if (BigDecimal.ZERO.compareTo(order.getPaymentAmount()) < 0) {//支付的金额是否大于0
+            //微信退款
+            RefundResult refund = PaymentUtil.refund(order.getOrderNumber(), "R" + order.getOrderNumber(), paymentAmount.doubleValue(),
+                    "/app/user/refundPayMoneyCallback");
+            if (!"100".equals(refund.getRa_Status())) {
+                return R.fail(refund.getRc_CodeMsg());//退款失败
+            }
+        }
+        return R.ok();
+    }
+
+    private void fillDateAndValue(UserStatsVO vo, LocalDateTime start, LocalDateTime end,
+                                  String datePattern, List<Map<String, Object>> stats) {
+        List<String> allDates = generateDateRange(start, end, datePattern);
+        Map<String, Integer> statMap = convertStatsToMap(stats);
+
+        List<String> dates = new ArrayList<>();
+        List<Integer> values = new ArrayList<>();
+
+        for (String date : allDates) {
+            dates.add(date);
+            values.add(statMap.getOrDefault(date, 0)); // 无数据的日期补0
+        }
+
+        vo.setDate(dates);
+        vo.setValue(values);
+    }
+
+    private Map<String, Integer> convertStatsToMap(List<Map<String, Object>> stats) {
+        return stats.stream()
+                .collect(Collectors.toMap(
+                        stat -> String.valueOf(stat.get("date")),
+                        stat -> Integer.valueOf(String.valueOf(stat.get("count")))
+                ));
+    }
+
+    private List<String> generateDateRange(LocalDateTime start, LocalDateTime end, String datePattern) {
+        List<String> dates = new ArrayList<>();
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);
+
+        TemporalUnit unit = getTemporalUnit(datePattern);
+        LocalDateTime current = getInitialDateTime(start, datePattern);
+
+        while (!current.isAfter(end)) {
+            dates.add(current.format(formatter));
+            current = incrementDateTime(current, unit);
+        }
+
+        return dates;
+    }
+
+    private TemporalUnit getTemporalUnit(String datePattern) {
+        switch (datePattern) {
+            case "HH时": return ChronoUnit.HOURS;
+            case "EEEE":
+            case "dd日":
+            case "yyyy-MM-dd": return ChronoUnit.DAYS;
+            case "MM月": return ChronoUnit.MONTHS;
+            default: return ChronoUnit.DAYS;
+        }
+    }
+
+    private LocalDateTime getInitialDateTime(LocalDateTime datetime, String datePattern) {
+        switch (datePattern) {
+            case "HH时": return datetime.withMinute(0).withSecond(0).withNano(0);
+            case "MM月": return datetime.withDayOfMonth(1);
+            default: return datetime;
+        }
+    }
+
+    private LocalDateTime incrementDateTime(LocalDateTime current, TemporalUnit unit) {
+        if (unit == ChronoUnit.HOURS) return current.plusHours(1);
+        if (unit == ChronoUnit.DAYS) return current.plusDays(1);
+        if (unit == ChronoUnit.MONTHS) return current.plusMonths(1);
+        return current.plusDays(1);
+    }
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/BannerServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/BannerServiceImpl.java
new file mode 100644
index 0000000..2fe995b
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/BannerServiceImpl.java
@@ -0,0 +1,80 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.Banner;
+import com.ruoyi.errand.mapper.BannerMapper;
+import com.ruoyi.errand.object.dto.sys.AddBannerDTO;
+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.service.BannerService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class BannerServiceImpl extends ServiceImpl<BannerMapper, Banner> implements BannerService {
+
+    @Override
+    public List<BannerVO> getBannerList() {
+        return this.getBaseMapper().getBannerList();
+
+    }
+
+    @Override
+    public void add(AddBannerDTO addBannerDTO) {
+        Long count = this.baseMapper.selectCount(new LambdaQueryWrapper<Banner>()
+                .eq(Banner::getName, addBannerDTO.getName()));
+        if (count > 0) {
+            throw new ServiceException("名称重复");
+        }
+        Banner banner = new Banner();
+        banner.setName(addBannerDTO.getName());
+        banner.setImageUrl(addBannerDTO.getImageUrl());
+        this.save(banner);
+    }
+
+    @Override
+    public IPage<BannerPageListVO> pageList(IPage<BannerPageListVO> page, String name) {
+        return this.baseMapper.pageList(page,name);
+    }
+
+    @Override
+    public void edit(EditBannerDTO editBannerDTO) {
+        Banner banner = this.getById(editBannerDTO.getId());
+        if (banner == null || banner.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("banner不存在");
+        }
+        banner.setName(editBannerDTO.getName());
+        banner.setImageUrl(editBannerDTO.getImageUrl());
+        this.baseMapper.updateById(banner);
+    }
+
+    @Override
+    public void delete(Integer id) {
+        Banner banner = this.getById(id);
+        if (banner == null || banner.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("banner不存在");
+        }
+        banner.setDelFlag(DelFlagConstant.DELETE);
+        this.baseMapper.updateById(banner);
+    }
+
+    @Override
+    public BannerDetailVo detail(Integer id) {
+        Banner banner = this.getById(id);
+        if (banner == null || banner.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("banner不存在");
+        }
+        BannerDetailVo bannerDetailVo = new BannerDetailVo();
+        BeanUtils.copyProperties(banner, bannerDetailVo);
+        return bannerDetailVo;
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityCourierServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityCourierServiceImpl.java
new file mode 100644
index 0000000..bb10729
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityCourierServiceImpl.java
@@ -0,0 +1,12 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.errand.domain.CommunityCourier;
+import com.ruoyi.errand.mapper.CommunityCourierMapper;
+import com.ruoyi.errand.service.CommunityCourierService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CommunityCourierServiceImpl extends ServiceImpl<CommunityCourierMapper, CommunityCourier> implements CommunityCourierService {
+
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityServiceImpl.java
new file mode 100644
index 0000000..2ae8dbb
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CommunityServiceImpl.java
@@ -0,0 +1,271 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.*;
+import com.ruoyi.errand.mapper.*;
+import com.ruoyi.errand.object.dto.sys.AddCommunityDTO;
+import com.ruoyi.errand.object.dto.sys.CommunityPageListDTO;
+import com.ruoyi.errand.object.dto.sys.EditCommunityDTO;
+import com.ruoyi.errand.object.vo.app.CommunityListVO;
+import com.ruoyi.errand.object.vo.sys.AllCommunityListVO;
+import com.ruoyi.errand.object.vo.sys.CommunityPageListVO;
+import com.ruoyi.errand.object.vo.sys.CommunitySysDetailVO;
+import com.ruoyi.errand.object.vo.sys.CourierPageListVO;
+import com.ruoyi.errand.service.CommunityService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+@Service
+public class CommunityServiceImpl extends ServiceImpl<CommunityMapper, Community> implements CommunityService {
+
+
+    private final CommunityCourierMapper communityCourierMapper;
+    private final OrderMapper orderMapper;
+    private final RegionMapper regionMapper;
+    private final CourierMapper courierMapper;
+    private final PhoneMapper phoneMapper;
+
+    public CommunityServiceImpl(CommunityCourierMapper communityCourierMapper, OrderMapper orderMapper, RegionMapper regionMapper, CourierMapper courierMapper, PhoneMapper phoneMapper) {
+        this.communityCourierMapper = communityCourierMapper;
+        this.orderMapper = orderMapper;
+        this.regionMapper = regionMapper;
+        this.courierMapper = courierMapper;
+        this.phoneMapper = phoneMapper;
+    }
+
+    @Override
+    public R<List<CommunityListVO>> getCommunity(Integer regionId) {
+        List<CommunityListVO> communityListVOS = this.getBaseMapper().selectListByRegionId(regionId);
+        return R.ok(communityListVOS);
+
+    }
+
+    @Override
+    public R<List<CommunityListVO>> getTotalCommunityList() {
+        List<CommunityListVO> communityListVOS = this.getBaseMapper().getTotalCommunityList();
+        return R.ok(communityListVOS);
+    }
+
+    @Override
+    public List<AllCommunityListVO> getAllCommunityList() {
+        List<Integer> list=communityCourierMapper.getAllCommunityList();
+        return this.baseMapper.getAllCommunityList(list);
+    }
+
+    @Override
+    public IPage<CommunityPageListVO> getCommunityPageList(CommunityPageListDTO dto) {
+        IPage<CommunityPageListVO> page = new Page<>(dto.getPageNum(),dto.getPageSize());
+        IPage<CommunityPageListVO> iPage=this.baseMapper.getCommunityPageList(page,dto);
+        iPage.getRecords().forEach(x->{
+            List<Order> orderList = orderMapper.selectList(new LambdaQueryWrapper<Order>()
+                    .eq(Order::getCommunityId, x.getId())
+                    .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+                    .eq(Order::getPayStatus,2));
+            if (orderList!=null&& !orderList.isEmpty()){
+                x.setTotal(orderList.size());//总订单数
+                LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
+                LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
+                long today = orderList.stream().filter(order -> order.getOrderTime().isAfter(start.minusNanos(1)))
+                        .filter(order -> order.getOrderTime().isBefore(end.plusNanos(1))).count();
+                x.setToday((int) today);//今日订单数
+            }
+        });
+
+        return iPage;
+    }
+
+    @Override
+    @Transactional
+    public void add(AddCommunityDTO addCommunityDTO) {
+
+        //城市地址是否存在
+        Region region = regionMapper.selectById(addCommunityDTO.getRegionId());
+        if (region==null){
+            throw new ServiceException("所在城市id错误");
+        }
+        //跑腿人员是否存在
+        Courier courier = courierMapper.selectById(addCommunityDTO.getCourierId());
+        if (courier==null|| Objects.equals(courier.getDelFlag(), DelFlagConstant.DELETE)){
+            throw new ServiceException("跑腿人员不存在");
+        }
+        //是否被绑定
+        Long count = communityCourierMapper.selectCount(new LambdaQueryWrapper<CommunityCourier>()
+                .eq(CommunityCourier::getCourierId, addCommunityDTO.getCourierId()));
+        if (count>0){
+            throw new ServiceException("跑腿人员已绑定");
+        }
+        //添加小区
+        Community community = new Community();
+        BeanUtils.copyProperties(addCommunityDTO,community);
+        this.save(community);
+        if (addCommunityDTO.getServicePhone()!=null){
+            //保存客服电话
+            Phone phone = new Phone();
+            phone.setType(2);
+            phone.setCommunity_id(community.getId());
+            phone.setPhone(addCommunityDTO.getServicePhone());
+            phoneMapper.insert(phone);
+        }
+        //保存跑腿员关系
+        CommunityCourier communityCourier = new CommunityCourier();
+        communityCourier.setCommunityId(community.getId());
+        communityCourier.setCourierId(addCommunityDTO.getCourierId());
+        communityCourier.setCreateTime(LocalDateTime.now());
+        communityCourierMapper.insert(communityCourier);
+
+    }
+
+    @Override
+    @Transactional
+    public void edit(EditCommunityDTO editCommunityDTO) {
+        //检查小区是否存在
+        Community community = this.baseMapper.selectById(editCommunityDTO.getId());
+        if (community==null|| Objects.equals(community.getDelFlag(), DelFlagConstant.DELETE)){
+            throw new ServiceException("该小区不存在");
+        }
+        //检查所在城市是否更改
+        if (!Objects.equals(editCommunityDTO.getRegionId(), community.getRegionId())){
+            //检查城市是否存在
+            Region region = regionMapper.selectById(editCommunityDTO.getRegionId());
+            if (region==null){
+                throw new ServiceException("所在城市id错误");
+            }
+        }
+        //检查客服电话是否更改
+        if (editCommunityDTO.getServicePhone()!=null){
+            Phone phone = phoneMapper.selectOne(new LambdaQueryWrapper<Phone>()
+                    .eq(Phone::getCommunity_id, editCommunityDTO.getId())
+                    .eq(Phone::getType, 2));
+            if (phone==null){
+                //添加
+                //保存客服电话
+                phone = new Phone();
+                phone.setType(2);
+                phone.setCommunity_id(community.getId());
+                phone.setPhone(editCommunityDTO.getServicePhone());
+                phoneMapper.insert(phone);
+            }else {
+                //修改
+                phone.setPhone(editCommunityDTO.getServicePhone());
+                phoneMapper.updateById(phone);
+            }
+
+        }
+        //检查跑腿员是否存在
+        Courier courier = courierMapper.selectById(editCommunityDTO.getCourierId());
+        if (courier==null|| Objects.equals(courier.getDelFlag(), DelFlagConstant.DELETE)){
+            throw new ServiceException("跑腿人员不存在");
+        }
+        //检查跑腿员是否绑定
+        Long count = communityCourierMapper.selectCount(new LambdaQueryWrapper<CommunityCourier>()
+                .eq(CommunityCourier::getCourierId, editCommunityDTO.getCourierId()));
+        if (count>0){
+            throw new ServiceException("跑腿人员已绑定");
+        }
+        //检查跑腿员是否更改
+        CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>()
+                .eq(CommunityCourier::getCommunityId, editCommunityDTO.getId()));
+        if (communityCourier==null){
+            //新增
+            communityCourier = new CommunityCourier();
+            communityCourier.setCommunityId(editCommunityDTO.getId());
+            communityCourier.setCourierId(editCommunityDTO.getCourierId());
+        }else {
+            //修改
+            communityCourier.setCourierId(editCommunityDTO.getCourierId());
+            communityCourierMapper.updateById(communityCourier);
+        }
+
+    }
+
+    @Override
+    public void delete(Integer id) {
+        Community community = this.baseMapper.selectById(id);
+        if (community==null|| Objects.equals(community.getDelFlag(), DelFlagConstant.DELETE)){
+            throw new ServiceException("未找到该小区");
+        }
+        //删除关系
+        CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>()
+                .eq(CommunityCourier::getCommunityId, id));
+       if (communityCourier!=null){
+           communityCourierMapper.deleteById(communityCourier.getId());
+       }
+        //删除小区
+       community.setDelFlag(DelFlagConstant.DELETE);
+       community.setUpdateTime(LocalDateTime.now());
+       this.baseMapper.updateById(community);
+    }
+
+    @Override
+    public void froze(Integer id) {
+        //冻结/解冻
+        Community community = this.baseMapper.selectById(id);
+        if (community==null|| Objects.equals(community.getDelFlag(), DelFlagConstant.DELETE)){
+            throw new ServiceException("未找到该小区");
+        }
+        community.setStatus(community.getStatus()==0?1:0);
+        community.setUpdateTime(LocalDateTime.now());
+        this.baseMapper.updateById(community);
+    }
+
+    @Override
+    public CommunitySysDetailVO detail(Integer id) {
+        Community community = this.getById(id);
+        if (community==null|| Objects.equals(community.getDelFlag(), DelFlagConstant.DELETE)){
+            throw new ServiceException("小区不存在");
+        }
+        CommunitySysDetailVO vo = new CommunitySysDetailVO();
+        BeanUtils.copyProperties(community,vo);
+        //
+        //所在城市Str 根据城市id查询整个字符
+        String regionStr =getAllStrById(community.getRegionId());
+        vo.setRegionStr(regionStr);
+        //跑腿员id和名称
+        CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>().eq(CommunityCourier::getCommunityId, community.getId()));
+        if (communityCourier!=null){
+            vo.setCourierId(communityCourier.getCourierId());
+            Courier courier = courierMapper.selectById(vo.getCourierId());
+            if (null!=courier && Objects.equals(courier.getDelFlag(), DelFlagConstant.UNDELETE)){
+                vo.setCourierName(courier.getName());
+            }
+        }
+        //客服电话
+        Phone phone = phoneMapper.selectOne(new LambdaQueryWrapper<Phone>().eq(Phone::getCommunity_id, community.getId()));
+        if (phone!=null){
+            vo.setServicePhone(phone.getPhone());
+        }
+        return vo;
+    }
+
+    private String getAllStrById(Integer regionId) {
+        if (regionId == null||regionId==0) {
+            return "";
+        }
+        Region region = regionMapper.selectById(regionId);
+        if (region == null) {
+            return "";
+        }
+        // 获取父级区域的完整名称
+        String parentName = getAllStrById(region.getParentId());
+        // 拼接当前区域名称
+        if (parentName.isEmpty()) {
+            return region.getName();
+        } else {
+            return parentName + "-" + region.getName();
+        }
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CourierServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CourierServiceImpl.java
new file mode 100644
index 0000000..09ff25c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/CourierServiceImpl.java
@@ -0,0 +1,380 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.*;
+import com.ruoyi.errand.mapper.*;
+import com.ruoyi.errand.object.dto.sys.AddCourierDTO;
+import com.ruoyi.errand.object.dto.sys.CourierPageListDTO;
+import com.ruoyi.errand.object.dto.sys.EditCourierDTO;
+import com.ruoyi.errand.object.vo.app.CompleteOrderDTO;
+import com.ruoyi.errand.object.vo.app.CourierInfoVO;
+import com.ruoyi.errand.object.vo.app.CourierOrderListVO;
+import com.ruoyi.errand.object.vo.app.CourierStatisticsVO;
+import com.ruoyi.errand.object.vo.sys.AllCourierListVO;
+import com.ruoyi.errand.object.vo.sys.AppUserPageListVO;
+import com.ruoyi.errand.object.vo.sys.CourierPageListVO;
+import com.ruoyi.errand.object.vo.sys.CourierSysDetailVO;
+import com.ruoyi.errand.service.AppUserService;
+import com.ruoyi.errand.service.CourierService;
+import com.ruoyi.errand.utils.RedisService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.temporal.TemporalAdjusters;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Service
+public class CourierServiceImpl extends ServiceImpl<CourierMapper, Courier> implements CourierService {
+
+    @Resource
+    private  EvaluationMapper evaluationMapper;
+    @Autowired
+    private OrderMapper orderMapper;
+    @Autowired
+    private CommunityCourierMapper communityCourierMapper;
+    @Autowired
+    private RedisService redisService;
+    @Autowired
+    private AppUserMapper appUserMapper;
+    @Autowired
+    private CommunityMapper communityMapper;
+
+
+    @Override
+    public CourierInfoVO getCourierInfo() {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        return this.baseMapper.getCourierInfo(appuser.getCourierId());
+    }
+
+    @Override
+    public CourierStatisticsVO getDatStatistics() {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+
+        //本日 本周 本月
+        CourierStatisticsVO vo =this.baseMapper.getDatStatistics(appuser.getCourierId());
+        //统计五星好评的订单数量
+        Integer fiveCount=evaluationMapper.getFiveCount(appuser.getCourierId());
+        vo.setFive(fiveCount);
+        return vo;
+    }
+
+    @Override
+    public IPage<CourierOrderListVO> getCourierOrderList(Integer pageNum, Integer pageSize, Integer orderStatus) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        IPage<CourierOrderListVO> page=new Page<>(pageNum, pageSize);
+        return this.baseMapper.getCourierOrderList(page,orderStatus,appuser.getCourierId());
+    }
+
+    @Override
+    public void receiveOrder(Integer id) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        if (appuser.getCourierId()==null){
+            throw new ServiceException("您还不是跑腿员");
+        }
+        CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>().eq(CommunityCourier::getCourierId, appuser.getCourierId()));
+        if(communityCourier==null){
+            throw new ServiceException("您未绑定小区");
+        }
+        //判断订单是否存在
+        Order order = orderMapper.selectById(id);
+        if (order==null || order.getDelFlag().equals(DelFlagConstant.DELETE)){
+            throw new ServiceException("订单不存在");
+        }
+        if (order.getOrderStatus()!=1){
+            throw new ServiceException("订单状态错误");
+        }
+        if (!order.getCommunityId().equals(communityCourier.getCommunityId())){
+            throw new ServiceException("该订单不属于您绑定的小区");
+        }
+
+        //修改订单状态
+        order.setOrderStatus(2);
+        order.setReceivingTime(LocalDateTime.now());
+        orderMapper.updateById(order);
+
+    }
+
+    @Override
+    public void completeOrder(CompleteOrderDTO completeOrderDTO) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        if (appuser.getCourierId()==null){
+            throw new ServiceException("您还不是跑腿员");
+        }
+        CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaUpdateWrapper<CommunityCourier>()
+                .eq(CommunityCourier::getCourierId, appuser.getCourierId()));
+        if(communityCourier==null){
+            throw new ServiceException("您未绑定小区");
+        }
+        //判断订单是否存在
+        Order order = orderMapper.selectById(completeOrderDTO.getId());
+        if (order==null || order.getDelFlag().equals(DelFlagConstant.DELETE)){
+            throw new ServiceException("订单不存在");
+        }
+        if (order.getOrderStatus()!=2){
+            throw new ServiceException("订单状态错误");
+        }
+        if (!order.getCommunityId().equals(communityCourier.getCommunityId())){
+            throw new ServiceException("该订单不属于您绑定的小区");
+        }
+
+        order.setOrderStatus(4);
+        order.setCourierPics(completeOrderDTO.getCourierPics());
+        order.setFinishTime(LocalDateTime.now());
+        orderMapper.updateById(order);
+        //评价
+        if (completeOrderDTO.getRating()!=null){
+            Evaluation evaluation = new Evaluation();
+            evaluation.setOrderId(order.getId());
+            evaluation.setType(1);
+            evaluation.setRating(completeOrderDTO.getRating());
+            if (null!=completeOrderDTO.getContent()){
+                evaluation.setContent(completeOrderDTO.getContent());
+            }
+            evaluationMapper.insert(evaluation);
+        }
+    }
+
+    @Override
+    public IPage<CourierPageListVO> getCourierPageList(CourierPageListDTO dto) {
+        IPage<CourierPageListVO> page = new Page<>(dto.getPageNum(),dto.getPageSize());
+        IPage<CourierPageListVO> iPage=this.baseMapper.getCourierPageList(page,dto);
+
+        iPage.getRecords().forEach(x->{
+            List<Order> orderList = orderMapper.selectList(new LambdaUpdateWrapper<Order>()
+                    .eq(Order::getCourierId, x.getId())
+                    .eq(Order::getDelFlag,DelFlagConstant.UNDELETE)
+                    .eq(Order::getPayMethod,2));
+            if (orderList!=null&& !orderList.isEmpty()){
+                //当天接单量
+                LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
+                LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
+                long todayNum = countByTimeRange(orderList, todayStart, todayEnd);
+                x.setToday((int) todayNum);
+                //本周接单量
+                LocalDate today = LocalDate.now();
+                LocalDate monday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
+                LocalDateTime weekStart = LocalDateTime.of(monday, LocalTime.MIN);
+                LocalDate sunday = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
+                LocalDateTime weekEnd = LocalDateTime.of(sunday, LocalTime.MAX);
+                long week = countByTimeRange(orderList, weekStart, weekEnd);
+                x.setWeek((int) week);
+                //本月接单量
+                LocalDate firstDay = today.withDayOfMonth(1);
+                LocalDateTime mouthStart = LocalDateTime.of(firstDay, LocalTime.MIN);
+                LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
+                LocalDateTime mouthEnd = LocalDateTime.of(lastDay, LocalTime.MAX);
+                long mouth = countByTimeRange(orderList, mouthStart, mouthEnd);
+                x.setMouth((int) mouth);
+                //五星好评数
+                List<Integer> orderIds = orderList.stream().map(Order::getId).collect(Collectors.toList());
+                Long five = evaluationMapper.selectCount(new LambdaQueryWrapper<Evaluation>()
+                        .in(Evaluation::getOrderId, orderIds)
+                        .eq(Evaluation::getType, 0)
+                        .eq(Evaluation::getRating, 5.0));
+                x.setFive(five.intValue());
+            }
+
+        });
+        return iPage;
+    }
+
+    @Override
+    public CourierSysDetailVO detail(Integer id) {
+        CourierSysDetailVO vo=this.baseMapper.detail(id);
+        List<Order> orderList = orderMapper.selectList(new LambdaUpdateWrapper<Order>()
+                .eq(Order::getCourierId, id)
+                .eq(Order::getDelFlag,DelFlagConstant.UNDELETE)
+                .eq(Order::getPayMethod,2));
+        if (orderList!=null&& !orderList.isEmpty()){
+            //当天接单量
+            LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
+            LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
+            long todayNum = countByTimeRange(orderList, todayStart, todayEnd);
+            vo.setToday((int) todayNum);
+            //本周接单量
+            LocalDate today = LocalDate.now();
+            LocalDate monday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
+            LocalDateTime weekStart = LocalDateTime.of(monday, LocalTime.MIN);
+            LocalDate sunday = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
+            LocalDateTime weekEnd = LocalDateTime.of(sunday, LocalTime.MAX);
+            long week = countByTimeRange(orderList, weekStart, weekEnd);
+            vo.setWeek((int) week);
+            //本月接单量
+            LocalDate firstDay = today.withDayOfMonth(1);
+            LocalDateTime mouthStart = LocalDateTime.of(firstDay, LocalTime.MIN);
+            LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
+            LocalDateTime mouthEnd = LocalDateTime.of(lastDay, LocalTime.MAX);
+            long mouth = countByTimeRange(orderList, mouthStart, mouthEnd);
+            vo.setMouth((int) mouth);
+            //五星好评
+            List<Integer> orderIds = orderList.stream().map(Order::getId).collect(Collectors.toList());
+            Long five = evaluationMapper.selectCount(new LambdaQueryWrapper<Evaluation>()
+                    .in(Evaluation::getOrderId, orderIds)
+                    .eq(Evaluation::getType, 0)
+                    .eq(Evaluation::getRating, 5.0));
+            vo.setFive(five.intValue());
+            //总共接单量
+            vo.setTotal(orderIds.size());
+        }
+
+
+        return vo;
+    }
+
+    @Override
+    public void add(AddCourierDTO addCourierDTO) {
+        AppUser appUser = appUserMapper.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, addCourierDTO.getPhone()).
+                eq(AppUser::getDelFlag, DelFlagConstant.UNDELETE));
+        if (appUser == null) {
+            throw new ServiceException("请先注册小程序");
+        }
+        //手机号是否存在
+        Courier courier = this.getBaseMapper().selectOne(new LambdaQueryWrapper<Courier>().eq(Courier::getPhone, addCourierDTO.getPhone())
+                .eq(Courier::getDelFlag, DelFlagConstant.UNDELETE));
+        if (courier != null) {
+            throw new ServiceException("该手机号的跑腿员已存在");
+        }
+        if (addCourierDTO.getCommunityId() != null) {
+            //绑定的小区是否存在
+            Community community = communityMapper.selectById(addCourierDTO.getCommunityId());
+            if (community == null|| Objects.equals(community.getDelFlag(), DelFlagConstant.DELETE)) {
+                throw new ServiceException("绑定的小区不存在");
+            }
+            //绑定的小区是否被别人绑定了
+            CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>()
+                    .eq(CommunityCourier::getCommunityId, addCourierDTO.getCommunityId()));
+            if (communityCourier != null) {
+                throw new ServiceException("该小区已被绑定");
+            }
+
+        }
+        //保存跑腿人员
+        Courier courier1 = new Courier();
+        BeanUtils.copyProperties(addCourierDTO, courier1);
+        this.save(courier1);
+        //绑定小区
+        if (addCourierDTO.getCommunityId() != null) {
+            CommunityCourier communityCourier = new CommunityCourier();
+            communityCourier.setCommunityId(addCourierDTO.getCommunityId());
+            communityCourier.setCourierId(courier1.getId());
+            communityCourierMapper.insert(communityCourier);
+        }
+    }
+
+    @Override
+    public void edit(EditCourierDTO editCourierDTO) {
+        Courier courier = this.getById(editCourierDTO.getId());
+        //检查是否存在
+        if (courier == null || courier.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("该跑腿员不存在");
+        }
+        //是否更改了手机号
+        if (!courier.getPhone().equals(editCourierDTO.getPhone())) {
+            //更改了手机号
+            AppUser appUser = appUserMapper.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, editCourierDTO.getPhone()).
+                    eq(AppUser::getDelFlag, DelFlagConstant.UNDELETE));
+            if (appUser == null) {
+                throw new ServiceException("请先注册小程序");
+            }
+            Courier courier1 = this.getBaseMapper().selectOne(new LambdaQueryWrapper<Courier>().eq(Courier::getPhone, editCourierDTO.getPhone())
+                    .eq(Courier::getDelFlag, DelFlagConstant.UNDELETE));
+            if (courier1 != null ) {
+                throw new ServiceException("该手机号的跑腿员已存在");
+            }
+        }
+        //是否更改了小区
+        CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>().eq(CommunityCourier::getCourierId, editCourierDTO.getId()));
+        if (communityCourier != null) {
+            //之前有绑定小区
+            if (editCourierDTO.getCommunityId() == null) {
+                //现在没有绑定,之前的要解绑
+                communityCourierMapper.deleteById(communityCourier.getId());
+
+            }else if (!editCourierDTO.getCommunityId().equals(communityCourier.getCommunityId())) {
+                //现在绑定了,不和之前的一样
+                //绑定的小区是否存在
+                Community community = communityMapper.selectById(editCourierDTO.getCommunityId());
+                if (community == null|| Objects.equals(community.getDelFlag(), DelFlagConstant.DELETE)) {
+                    throw new ServiceException("绑定的小区不存在");
+                }
+                //绑定的小区是否被别人绑定了
+                CommunityCourier communityCourier1 = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>()
+                        .eq(CommunityCourier::getCommunityId, editCourierDTO.getCommunityId()));
+                if (communityCourier1 != null) {
+                    throw new ServiceException("该小区已被绑定");
+                }
+                //修改
+                communityCourier.setCommunityId(editCourierDTO.getCommunityId());
+                communityCourier.setUpdateTime(LocalDateTime.now());
+                communityCourierMapper.updateById(communityCourier);
+            }
+        }
+        //修改
+        BeanUtils.copyProperties(editCourierDTO, courier);
+        this.updateById(courier);
+
+    }
+
+    @Override
+    public void froze(Integer id) {
+        Courier  courier= this.getById(id);
+        if (courier == null||courier.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("该跑腿员不存在");
+        }
+        courier.setStatus(courier.getStatus()==0?1:0);
+        courier.setUpdateTime(LocalDateTime.now());
+        this.updateById(courier);
+    }
+
+    @Override
+    public void delete(Integer id) {
+        Courier  courier= this.getById(id);
+        if (courier == null||courier.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            throw new ServiceException("该跑腿员不存在");
+        }
+        //判断跑腿员是否绑定了小区
+        CommunityCourier communityCourier = communityCourierMapper.selectOne(new LambdaQueryWrapper<CommunityCourier>().eq(CommunityCourier::getCourierId, id));
+        if (communityCourier != null) {
+            communityCourierMapper.deleteById(communityCourier.getId());//解绑
+        }
+        courier.setDelFlag(DelFlagConstant.DELETE);
+        courier.setUpdateTime(LocalDateTime.now());
+        this.updateById(courier);
+
+    }
+
+    @Override
+    public List<AllCourierListVO> getAllCourierList() {
+        List<Integer> couriers= communityCourierMapper.getAllCourierList();
+        return this.baseMapper.getAllCourierList(couriers);
+
+    }
+
+    private static long countByTimeRange(List<Order> orderList, LocalDateTime start, LocalDateTime end) {
+        return orderList.stream()
+                .filter(order -> order.getOrderTime() != null) // 过滤空时间
+                .filter(order -> order.getOrderTime().isAfter(start.minusNanos(1))) // 大于等于 start
+                .filter(order -> order.getOrderTime().isBefore(end.plusNanos(1))) // 小于等于 end
+                .count();
+    }
+
+
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java
new file mode 100644
index 0000000..5deedf4
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java
@@ -0,0 +1,58 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.domain.Evaluation;
+import com.ruoyi.errand.domain.Order;
+import com.ruoyi.errand.mapper.EvaluationMapper;
+import com.ruoyi.errand.mapper.OrderMapper;
+import com.ruoyi.errand.object.dto.app.AddEvaluationDTO;
+import com.ruoyi.errand.service.EvaluationService;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Service
+public class EvaluationServiceImpl extends ServiceImpl<EvaluationMapper, Evaluation> implements EvaluationService {
+
+    private  OrderMapper orderMapper;
+
+    @Override
+    public void add(AddEvaluationDTO addEvaluationDTO) {
+        //检查订单id是否存在
+        //该用户类型是否正常
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //判断订单是否存在
+        Order order = orderMapper.selectById(addEvaluationDTO.getOrderId());
+        if (order==null ||order.getDelFlag().equals(DelFlagConstant.DELETE)){
+            throw new ServiceException("订单id错误");
+        }
+        if (order.getOrderStatus()!=4){
+            throw new ServiceException("订单状态错误");
+        }
+
+        if (addEvaluationDTO.getType()==0){
+            //用户评价
+            if (!Objects.equals(order.getAppUserId(), appuser.getId())){
+                throw new ServiceException("用户与订单不匹配");
+            }
+        }else if (addEvaluationDTO.getType()==1){
+            if (!Objects.equals(order.getCourierId(), appuser.getCourierId())){
+                throw new ServiceException("跑腿与订单不匹配");
+            }
+        }else {
+            throw new ServiceException("评价类型错误");
+        }
+        Evaluation evaluation = new Evaluation();
+        evaluation.setOrderId(addEvaluationDTO.getOrderId());
+        evaluation.setType(addEvaluationDTO.getType());
+        evaluation.setRating(addEvaluationDTO.getRating());
+        evaluation.setContent(addEvaluationDTO.getContent());
+        evaluation.setCreateTime(LocalDateTime.now());
+        this.save(evaluation);
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/FeedbackServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/FeedbackServiceImpl.java
new file mode 100644
index 0000000..b78c3e1
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/FeedbackServiceImpl.java
@@ -0,0 +1,65 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.constant.ScheduleConstants;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.constant.StatusConstant;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.domain.Feedback;
+import com.ruoyi.errand.mapper.FeedbackMapper;
+import com.ruoyi.errand.object.dto.sys.FeedbackPageListDTO;
+import com.ruoyi.errand.object.vo.sys.BannerPageListVO;
+import com.ruoyi.errand.object.vo.sys.FeedbackPageListVO;
+import com.ruoyi.errand.service.FeedbackService;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+@Service
+public class FeedbackServiceImpl extends ServiceImpl<FeedbackMapper, Feedback> implements FeedbackService {
+
+    @Override
+    public void add(String content) {
+        Feedback feedback = new Feedback();
+        feedback.setContent(content);
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        feedback.setAppUserId(appuser.getId());
+        feedback.setName(appuser.getName());
+        feedback.setPhone(appuser.getPhone());
+        feedback.setStatus(StatusConstant.UN_DISPOSE);
+        this.save(feedback);
+
+    }
+
+    @Override
+    public IPage<FeedbackPageListVO> getFeedbackPageList(FeedbackPageListDTO dto) {
+        IPage<FeedbackPageListVO> page=new Page<>(dto.getPageNum(),dto.getPageSize());
+        return this.baseMapper.getFeedbackPageList(page,dto);
+    }
+
+    @Override
+    public void delete(Integer id) {
+        Feedback feedback = this.getById(id);
+        if (feedback==null||feedback.getDelFlag().equals(DelFlagConstant.DELETE)){
+            throw new ServiceException("该反馈不存在");
+        }
+        feedback.setDelFlag(DelFlagConstant.DELETE);
+        this.updateById(feedback);
+    }
+
+    @Override
+    public void dispose(Integer id) {
+        Feedback feedback = this.getById(id);
+        if (feedback==null||feedback.getDelFlag().equals(DelFlagConstant.DELETE)){
+            throw new ServiceException("该反馈不存在");
+        }
+        feedback.setStatus(StatusConstant.DISPOSE);
+        LoginUser loginUser  = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        feedback.setHandlerId(loginUser.getUserId());
+        this.updateById(feedback);
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java
new file mode 100644
index 0000000..a6ebf4c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java
@@ -0,0 +1,607 @@
+package com.ruoyi.errand.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.AddressBook;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.domain.Community;
+import com.ruoyi.errand.domain.Order;
+import com.ruoyi.errand.mapper.AddressBookMapper;
+import com.ruoyi.errand.mapper.AppUserMapper;
+import com.ruoyi.errand.mapper.CommunityMapper;
+import com.ruoyi.errand.mapper.OrderMapper;
+import com.ruoyi.errand.object.dto.app.ConfirmOrderDTO;
+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.vo.app.AppUserOrderListVO;
+import com.ruoyi.errand.object.vo.app.ConfirmOrderVO;
+import com.ruoyi.errand.object.vo.app.OrderDetailVO;
+import com.ruoyi.errand.object.vo.app.OrderTopInfoVO;
+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.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
+
+    @Resource
+    private AddressBookMapper addressBookMapper;
+
+    @Resource
+    private CommunityMapper communityMapper;
+    @Autowired
+    private RedisTemplate redisTemplate;
+    @Autowired
+    private AppUserMapper appUserMapper;
+    @Resource
+    private DeliveryWebSocket deliveryWebSocket;
+
+    @Override
+    public ConfirmOrderVO confirmOrder(ConfirmOrderDTO confirmOrderDTO) {
+
+        ConfirmOrderVO confirmOrderVO = new ConfirmOrderVO();
+        BeanUtils.copyProperties(confirmOrderDTO, confirmOrderVO);
+
+        //小区是否存在
+        Community community = communityMapper.selectById(confirmOrderDTO.getCommodityId());
+        if (community==null||community.getDelFlag().equals(DelFlagConstant.DELETE)){
+            throw new ServiceException("小区不存在");
+        }
+        if (community.getStatus()==1){
+            throw new ServiceException("小区已被冻结");
+        }
+        //将地址簿信息查询出来
+        AddressBook addressBook = addressBookMapper.selectById(confirmOrderDTO.getAddressBookId());
+        confirmOrderVO.setRecipientName(addressBook.getRecipientName());
+        confirmOrderVO.setRecipientPhone(addressBook.getRecipientPhone());
+        confirmOrderVO.setAddressDetail(addressBook.getAddressDetail());
+
+        //查询用户信息,设置用户支付信息
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        confirmOrderVO.setCommunityId(confirmOrderDTO.getCommodityId());
+        confirmOrderVO.setCommunityName(community.getName());
+        //两种支付方式
+        if (confirmOrderDTO.getPayMethod()==0){
+            confirmOrderVO.setPayMethod(0);
+            //在线支付
+            confirmOrderVO.setOrderAmount(community.getFeeAmount());//订单金额
+            confirmOrderVO.setPaymentAmount(community.getFeeAmount());//支付金额
+
+        }else if (confirmOrderDTO.getPayMethod()==1){
+            confirmOrderVO.setPayMethod(1);
+            //会员支付
+            if (appuser.getEndTime().isAfter(LocalDateTime.now())) {
+                //过期了
+                throw new ServiceException("支付错误:会员已到期");
+            }
+            confirmOrderVO.setOrderAmount(community.getFeeAmount());
+            confirmOrderVO.setPaymentAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));//支付金额
+
+        }else {
+            throw new ServiceException("支付方式错误");
+        }
+
+        return confirmOrderVO;
+
+
+    }
+
+    @Override
+    public R orderPayment(ConfirmOrderDTO confirmOrderDTO) {
+        Order order = new Order();
+        BeanUtils.copyProperties(confirmOrderDTO, order);
+        //小区是否存在
+        Community community = communityMapper.selectById(confirmOrderDTO.getCommodityId());
+        if (community==null||community.getDelFlag().equals(DelFlagConstant.DELETE)){
+            throw new ServiceException("小区不存在");
+        }
+        if (community.getStatus()==1){
+            throw new ServiceException("小区已被冻结");
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+        order.setOrderNumber("QJS" + getNumber(3) + sdf.format(new Date()));
+
+        //将地址簿信息查询出来
+        AddressBook addressBook = addressBookMapper.selectById(confirmOrderDTO.getAddressBookId());
+        order.setRecipientName(addressBook.getRecipientName());
+        order.setRecipientPhone(addressBook.getRecipientPhone());
+        order.setAddressDetail(addressBook.getAddressDetail());
+
+        //查询用户信息,设置用户支付信息
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        if (appuser.getFirstOrder()==1){
+            appuser.setFirstOrder(0);
+            appUserMapper.updateById(appuser);
+        }
+        order.setAppUserId(appuser.getId());
+        order.setCommunityId(confirmOrderDTO.getCommodityId());
+        order.setCommunityName(community.getName());
+
+        order.setPayMethod(confirmOrderDTO.getPayMethod());//支付方式
+        order.setPayStatus(1);//待付款
+
+        //两种支付方式
+        if (confirmOrderDTO.getPayMethod()==0){
+            order.setPayMethod(0);
+            //在线支付
+            order.setOrderAmount(community.getFeeAmount());//订单金额
+            order.setPaymentAmount(community.getFeeAmount());//支付金额
+
+        }else if (confirmOrderDTO.getPayMethod()==1){
+            order.setPayMethod(1);
+            if (appuser.getEndTime().isBefore(LocalDateTime.now())) {
+                //过期了
+                throw new ServiceException("支付错误:会员已到期");
+            }
+            order.setOrderAmount(community.getFeeAmount());
+            order.setPaymentAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));//支付金额
+        }else {
+            throw new ServiceException("支付方式错误");
+        }
+        this.save(order);//先保存订单
+
+
+        //判断需要支付的金额是否大于0
+        BigDecimal paymentMoney = order.getPaymentAmount();
+        if ( BigDecimal.ZERO.compareTo(paymentMoney) < 0){
+            //调起微信支付
+            String agencyMatters = order.getAgencyMatters();
+            UniPayResult uniPayResult = PaymentUtil.uniPay(order.getOrderNumber(), paymentMoney.doubleValue(),  "代办事项",
+                    agencyMatters, "", "/app/order/orderPaymentCallback", appuser.getWxOpenid(), null);
+            if(null == uniPayResult || !"100".equals(uniPayResult.getRa_Code())){
+                return R.fail(null == uniPayResult ? "支付失败" : uniPayResult.getRb_CodeMsg());
+            }
+            String rc_result = uniPayResult.getRc_Result();
+            JSONObject jsonObject = JSON.parseObject(rc_result);
+            jsonObject.put("orderId", order.getId().toString());
+            //将支付数据添加到redis队列中,便于定时任务去校验是否完成支付,没有完成支付支付,15分钟后关闭订单。
+            long second = LocalDateTime.now().plusMinutes(15).toEpochSecond(ZoneOffset.UTC);
+            redisTemplate.opsForZSet().add("OrderPayment", order.getOrderNumber(), second);
+            return R.ok(jsonObject.toJSONString());
+        }
+
+        //会员支付或支付金额为0直接支付成功
+        order.setOrderStatus(1);//待确认
+        order.setPayStatus(2);//已支付
+        order.setOrderTime(LocalDateTime.now());//下单时间
+        this.updateById(order);
+        Map<String,Object> courier= appUserMapper.getCourierByCommunityId(order.getCommunityId());
+        if (courier == null || courier.isEmpty()) {
+            log.warn("未找到社区ID={}对应的骑手", order.getCommunityId());
+            return R.ok();
+        }
+        //todo 短信通知
+        sendSmsNotificationToCourier(courier,order);
+        //小程序弹窗通知
+        notifyDeliveryPerson((Long) courier.get("id"), order,1);//1=新订单
+
+        //登录访问
+        return R.ok(order.getId().toString());
+    }
+
+
+    /**
+     * 发送短信通知给骑手
+     */
+    private void sendSmsNotificationToCourier(Map<String, Object> courier, Order order) {
+        try {
+            String phoneNumber = (String) courier.get("phone");
+            if (StringUtils.isBlank(phoneNumber)) {
+                log.warn("骑手手机号为空,无法发送短信通知");
+                return;
+            }
+//            String content = String.format("您有新的跑腿订单(订单号:%s),请及时处理!", order.getOrderNumber());
+            //todo  调用短信服务API
+//            SMSUtil.sendSms(order.getOrderNumber(),phoneNumber, "", "");
+            log.info("已发送短信通知给骑手: {}", phoneNumber);
+        } catch (Exception e) {
+            log.error("发送短信通知失败", e);
+        }
+    }
+
+    /**
+     * 发送弹窗通知
+     */
+    private void notifyDeliveryPerson(Long deliveryPersonId, Order order,Integer type) {
+        JSONObject message = new JSONObject();
+        message.put("type",type==1?"new_order":"update_order");
+        message.put("orderId", order.getId());
+        message.put("orderTime", order.getOrderTime());
+        deliveryWebSocket.sendNotification(deliveryPersonId.toString(), message.toJSONString());
+    }
+
+    @Override
+    public R orderPaymentCallback(UniPayCallbackResult uniPayCallbackResult) {
+        Order order = this.getBaseMapper().selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, uniPayCallbackResult.getR2_OrderNo()));
+        if(null == order || order.getPayStatus() == 2){
+            return R.ok();
+        }
+        order.setOrderStatus(1);//待确认
+        order.setPayStatus(2);//已支付
+        order.setOrderTime(LocalDateTime.now());//下单时间
+        String r7TrxNo = uniPayCallbackResult.getR9_BankTrxNo();
+        order.setSerialNumber(r7TrxNo);
+        this.updateById(order);
+
+        Map<String,Object> courier= appUserMapper.getCourierByCommunityId(order.getCommunityId());
+        if (courier == null || courier.isEmpty()) {
+            log.warn("未找到社区ID={}对应的骑手", order.getCommunityId());
+            return R.ok();
+        }
+        //todo 短信通知
+        sendSmsNotificationToCourier(courier,order);
+        //小程序弹窗通知
+        notifyDeliveryPerson((Long) courier.get("id"), order,1);//1=新订单
+        return R.ok();
+    }
+
+    /**
+     * 定时任务关闭订单
+     */
+    @Override
+    public void closeOrder() {
+        //订单支付数据
+        long second = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
+        Set<String> orderPayment = redisTemplate.opsForZSet().rangeByScore("OrderPayment", 0, second);
+        if(orderPayment.size() > 0){
+            List<Order> list = this.getBaseMapper().selectList(new LambdaQueryWrapper<Order>().in(Order::getOrderNumber, orderPayment));
+            for (Order order : list) {
+                if(null == order || order.getPayStatus() != 1){
+                    //不在待支付中的,移除
+                    redisTemplate.opsForZSet().remove("OrderPayment", order.getOrderNumber());
+                    continue;
+                }
+                //开始执行关闭订单操作
+                CloseOrderResult closeOrderResult = PaymentUtil.closeOrder(order.getOrderNumber());
+                if((null == closeOrderResult || !closeOrderResult.getRa_Status().equals("100")) &&
+                        Arrays.asList("0", "4", "101", "10080000", "10080002", "10083004", "10083005").contains(closeOrderResult.getRb_Code())){
+                    redisTemplate.opsForZSet().add("OrderPayment", order.getOrderNumber(), 0);
+                    log.error("关闭订单失败:{}---->{}", order.getOrderNumber(), JSON.toJSONString(closeOrderResult));
+                }
+                redisTemplate.opsForZSet().remove("OrderPayment", order.getOrderNumber());
+                //删除订单
+                order.setDelFlag(DelFlagConstant.DELETE);
+                this.updateById(order);
+            }
+        }
+
+    }
+
+    @Override
+    public IPage<AppUserOrderListVO> getAppUserOrderList(Integer pageNum, Integer pageSize, Integer orderStatus) {
+        IPage<Order> page = new Page<>(pageNum, pageSize);
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        return this.baseMapper.getAppUserOrderList(page,orderStatus,appuser.getId());
+    }
+
+    @Override
+    public OrderDetailVO getOrderDetail(Integer id) {
+        return this.baseMapper.getOrderDetail(id);
+    }
+
+    @Override
+    public void setOrderInfo(SetConfirmOrderDTO setConfirmOrderDTO) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //判断订单是否存在
+        Order order = this.getById(setConfirmOrderDTO.getId());
+        if (order==null
+                ||order.getDelFlag().equals(DelFlagConstant.DELETE)
+                ||!Objects.equals(order.getAppUserId(), appuser.getId())){
+            throw new ServiceException("订单id错误");
+        }
+        if (order.getOrderStatus()!=1){
+            throw new ServiceException("订单状态错误");
+        }
+        //判断地址簿id是否存在
+        if (setConfirmOrderDTO.getAddressBookId()!=null){
+            AddressBook addressBook = addressBookMapper.selectById(setConfirmOrderDTO.getAddressBookId());
+            if (addressBook==null||addressBook.getDel_flag().equals(DelFlagConstant.DELETE)){
+                throw new ServiceException("该地址簿id不存在");
+            }
+            if (!Objects.equals(addressBook.getCommunityId(), order.getCommunityId())){
+                throw new ServiceException("不能更改为其他小区");
+            }
+            order.setRecipientPhone(addressBook.getRecipientPhone());
+            order.setRecipientName(addressBook.getRecipientName());
+            order.setAddressDetail(addressBook.getAddressDetail());
+        }
+        //代办事项等更改
+        if (setConfirmOrderDTO.getNum()!=null){
+            order.setNum(setConfirmOrderDTO.getNum());
+        }
+        if (setConfirmOrderDTO.getRemark()!=null){
+            order.setRemark(setConfirmOrderDTO.getRemark());
+        }
+        if (setConfirmOrderDTO.getAgencyMatters()!=null){
+            order.setAgencyMatters(setConfirmOrderDTO.getAgencyMatters());
+        }
+        if (setConfirmOrderDTO.getPics()!=null){
+            order.setPics(setConfirmOrderDTO.getPics());
+        }
+        //修改
+        this.updateById(order);
+
+        Map<String,Object> courier= appUserMapper.getCourierByCommunityId(order.getCommunityId());
+        if (courier == null || courier.isEmpty()) {
+            log.warn("未找到社区ID={}对应的骑手", order.getCommunityId());
+            return;
+        }
+        //小程序弹窗通知
+        notifyDeliveryPerson((Long) courier.get("id"), order,2);//2=修改订单
+    }
+
+    @Override
+    public void cancelOrder(Integer id) {
+        //判断订单
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //判断订单是否存在
+        Order order = this.getById(id);
+        if (order==null
+                ||order.getDelFlag().equals(DelFlagConstant.DELETE)
+                ||!Objects.equals(order.getAppUserId(), appuser.getId())){
+            throw new ServiceException("订单id错误");
+        }
+        //判断状态
+        if (order.getOrderStatus()!=1){
+            throw new ServiceException("订单状态错误");
+        }
+        //判断支付类型
+        if ( order.getPayMethod()==0){
+          //在线支付  回退
+            order.setOrderStatus(3);//已取消
+            R r = refundPayMoney(order);//退款
+            if (200 == r.getCode()) {
+                this.updateById(order);
+            }
+        }else {
+            order.setOrderStatus(3);//已取消
+            this.updateById(order);
+        }
+
+    }
+
+    @Override
+    public R refundPayMoneyCallback(RefundCallbackResult refundCallbackResult) {
+        String code = refundCallbackResult.getR3_RefundOrderNo().substring(1);
+        Order order = this.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, code));
+        if (null == order || order.getPayStatus() == 1 || order.getOrderStatus() == 6) {
+            return R.ok();
+        }
+        order.setRefundCode(refundCallbackResult.getR5_RefundTrxNo());
+        order.setRefundStatus(2);
+        order.setRefundTime(LocalDateTime.now());
+        this.updateById(order);
+        return R.ok();
+    }
+
+    @Override
+    public OrderTopInfoVO orderTopInfo(Integer communityId) {
+
+        OrderTopInfoVO orderTopInfoVO = new OrderTopInfoVO();
+        //总金额
+        Map<String, Object> total = this.baseMapper.getOrderTopInfoByDate(communityId,null,null);
+        orderTopInfoVO.setTodayOrderNum(Integer.valueOf(String.valueOf(total.get("num"))) );
+        orderTopInfoVO.setTotalOrderAmount(new BigDecimal(String.valueOf(total.get("amount"))));
+
+        //今日
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime start = now.with(LocalTime.MIN);
+        LocalDateTime end = now.with(LocalTime.MAX);
+        Map<String, Object> today = this.baseMapper.getOrderTopInfoByDate(communityId,start,end);
+        orderTopInfoVO.setTodayOrderNum(Integer.valueOf(String.valueOf(today.get("num"))));
+        orderTopInfoVO.setTodayOrderAmount(new BigDecimal(String.valueOf(today.get("amount"))));
+
+        //当月
+        YearMonth currentMonth = YearMonth.now();
+        start = currentMonth.atDay(1).atStartOfDay();
+        end = currentMonth.atEndOfMonth().atTime(LocalTime.MAX);
+        Map<String, Object> mouth = this.baseMapper.getOrderTopInfoByDate(communityId,start,end);
+        orderTopInfoVO.setMouthOrderNum(Integer.valueOf(String.valueOf(mouth.get("num"))));
+        orderTopInfoVO.setMouthOrderAmount(new BigDecimal(String.valueOf(mouth.get("amount"))));
+
+        //本季度
+        YearMonth thisMonth = YearMonth.now();
+        YearMonth firstMonthOfQuarter = thisMonth.with(
+                Month.of(((thisMonth.getMonthValue() - 1) / 3) * 3 + 1));
+        YearMonth lastMonthOfQuarter = firstMonthOfQuarter.plusMonths(2);
+        start =  firstMonthOfQuarter.atDay(1).atStartOfDay();
+        end = lastMonthOfQuarter.atEndOfMonth().atTime(LocalTime.MAX) ;
+        Map<String, Object> quarter = this.baseMapper.getOrderTopInfoByDate(communityId,start,end);
+        orderTopInfoVO.setQuarterOrderNum(Integer.valueOf(String.valueOf(quarter.get("num"))));
+        orderTopInfoVO.setQuarterOrderAmount(new BigDecimal(String.valueOf(quarter.get("amount"))));
+
+        //本年
+        int year = Year.now().getValue();
+        start =  LocalDateTime.of(year, 1, 1, 0, 0); // 1月1日
+        end =   LocalDateTime.of(year, 12, 31, 23, 59, 59); // 12月31日
+        Map<String, Object> currentYear = this.baseMapper.getOrderTopInfoByDate(communityId,start,end);
+        orderTopInfoVO.setYearOrderNum(Integer.valueOf(String.valueOf(currentYear.get("num"))));
+        orderTopInfoVO.setYearOrderAmount(new BigDecimal(String.valueOf(currentYear.get("amount"))));
+        return orderTopInfoVO;
+    }
+
+    @Override
+    public OrderStatsVO getOrderStats(LocalDateTime start, LocalDateTime end, String datePattern, Integer communityId) {
+        OrderStatsVO orderStatsVO = new OrderStatsVO();
+        // 1. 查询按时间分组的用户数
+        List<Map<String, Object>> stats = this.getBaseMapper().countGroupByDate(start, end, datePattern , communityId);
+
+        // 2. 生成完整的日期序列并填充数据
+        fillDateAndValue(orderStatsVO, start, end, datePattern, stats);
+
+        return orderStatsVO;
+    }
+
+    @Override
+    public IPage<FinanceStatisticsVO> financeStatistics(FinanceStatisticsDTO dto) {
+        //查询出总的数量
+        IPage<FinanceStatisticsVO> page = new Page<>();
+        page.setTotal(this.getBaseMapper().selectPageTotal(dto));
+        List<FinanceStatisticsVO> list=this.getBaseMapper().financeStatistics(dto);
+        page.setRecords(list);
+        page.setSize(dto.getPageSize());
+        page.setCurrent(dto.getPageNum());
+        return page;
+    }
+
+    @Override
+    public List<FinanceStatisticsVO> export(FinanceStatisticsDTO dto) {
+        return this.getBaseMapper().export(dto);
+    }
+
+
+
+    @Override
+    public IPage<OrderPageListVO> getOrderPageList(OrderPageListDTO dto) {
+        IPage<OrderPageListVO> page = new Page<>(dto.getPageNum(),dto.getPageSize());
+        return this.getBaseMapper().getOrderPageList(page,dto);
+    }
+
+    @Override
+    public OrderSysDetailVO detail(Integer id) {
+        return this.getBaseMapper().detail(id);
+    }
+
+    @Override
+    public List<OrderPageListVO> orderExport(OrderPageListDTO dto) {
+
+        return this.getBaseMapper().orderExport(dto);
+    }
+
+    /**
+     * 填充空余时间
+     */
+    private void fillDateAndValue(OrderStatsVO vo, LocalDateTime start, LocalDateTime end,
+                                  String datePattern, List<Map<String, Object>> stats) {
+        List<String> allDates = generateDateRange(start, end, datePattern);
+        Map<String, Integer> numMap = convertNumsToMap(stats);
+        Map<String, BigDecimal> amountMap = convertAmountToMap(stats);
+
+        List<String> dates = new ArrayList<>();
+        List<Integer> nums = new ArrayList<>();
+
+        List<BigDecimal> amounts = new ArrayList<>();
+
+        for (String date : allDates) {
+            dates.add(date);
+            nums.add(numMap.getOrDefault(date, 0)); // 无数据的日期补0
+            amounts.add(amountMap.getOrDefault(date,BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP)));
+        }
+
+        vo.setDate(dates);
+        vo.setNumList(nums);
+        vo.setAmountList(amounts);
+    }
+    private Map<String, Integer> convertNumsToMap(List<Map<String, Object>> stats) {
+        return stats.stream()
+                .collect(Collectors.toMap(
+                        stat -> String.valueOf(stat.get("date")),
+                        stat -> Integer.valueOf(String.valueOf(stat.get("num")))
+                ));
+    }
+
+    private Map<String, BigDecimal> convertAmountToMap(List<Map<String, Object>> stats) {
+        return stats.stream()
+                .collect(Collectors.toMap(
+                        stat -> String.valueOf(stat.get("date")),
+                        stat -> new BigDecimal(String.valueOf(stat.get("amount")))
+                ));
+    }
+
+    private List<String> generateDateRange(LocalDateTime start, LocalDateTime end, String datePattern) {
+        List<String> dates = new ArrayList<>();
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);
+
+        TemporalUnit unit = getTemporalUnit(datePattern);
+        LocalDateTime current = getInitialDateTime(start, datePattern);
+
+        while (!current.isAfter(end)) {
+            dates.add(current.format(formatter));
+            current = incrementDateTime(current, unit);
+        }
+
+        return dates;
+    }
+
+    private TemporalUnit getTemporalUnit(String datePattern) {
+        switch (datePattern) {
+            case "HH时": return ChronoUnit.HOURS;
+            case "EEEE":
+            case "dd日":
+            case "yyyy-MM-dd": return ChronoUnit.DAYS;
+            case "MM月": return ChronoUnit.MONTHS;
+            default: return ChronoUnit.DAYS;
+        }
+    }
+
+    private LocalDateTime getInitialDateTime(LocalDateTime datetime, String datePattern) {
+        switch (datePattern) {
+            case "HH时": return datetime.withMinute(0).withSecond(0).withNano(0);
+            case "MM月": return datetime.withDayOfMonth(1);
+            default: return datetime;
+        }
+    }
+
+    private LocalDateTime incrementDateTime(LocalDateTime current, TemporalUnit unit) {
+        if (unit == ChronoUnit.HOURS) return current.plusHours(1);
+        if (unit == ChronoUnit.DAYS) return current.plusDays(1);
+        if (unit == ChronoUnit.MONTHS) return current.plusMonths(1);
+        return current.plusDays(1);
+    }
+    /**
+     * 返回订单支付金额
+     */
+    public R refundPayMoney(Order order) {
+        //开始退款
+        BigDecimal paymentAmount = order.getPaymentAmount();
+        if (BigDecimal.ZERO.compareTo(order.getPaymentAmount()) < 0) {//支付的金额是否大于0
+            //微信退款
+            RefundResult refund = PaymentUtil.refund(order.getOrderNumber(), "R" + order.getOrderNumber(), paymentAmount.doubleValue(),
+                    "/app/order/refundPayMoneyCallback");
+            if (!"100".equals(refund.getRa_Status())) {
+                return R.fail(refund.getRc_CodeMsg());//退款失败
+            }
+        }
+        return R.ok();
+    }
+
+
+    public String getNumber(Integer size){
+        StringBuilder str = new StringBuilder();
+        for (int i = 0; i < size; i++) {
+            str.append(Double.valueOf(Math.random() * 10).intValue());
+        }
+        return str.toString();
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/PhoneServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/PhoneServiceImpl.java
new file mode 100644
index 0000000..5b33142
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/PhoneServiceImpl.java
@@ -0,0 +1,55 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.domain.Community;
+import com.ruoyi.errand.domain.Phone;
+import com.ruoyi.errand.mapper.CommunityMapper;
+import com.ruoyi.errand.mapper.PhoneMapper;
+import com.ruoyi.errand.service.PhoneService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Service
+public class PhoneServiceImpl extends ServiceImpl<PhoneMapper, Phone> implements PhoneService {
+    @Override
+    public String getServletPhone() {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //查找小区客服
+        Phone phone = this.getBaseMapper().selectOne(new LambdaUpdateWrapper<Phone>().eq(Phone::getType, 2)
+                .eq(Phone::getCommunity_id, appuser.getCommunityId()));
+        if (phone!=null){
+            return phone.getPhone();
+        }
+        //查找平台客服
+        List<Phone> list = this.getBaseMapper().selectList(new LambdaUpdateWrapper<Phone>().eq(Phone::getType, 1));
+        return list.get(0).getPhone();
+    }
+
+    @Override
+    public void saveServicePhone(String phone) {
+        Phone phoneEntity = this.getBaseMapper().selectOne(new LambdaUpdateWrapper<Phone>().eq(Phone::getType, 1));
+        if (phoneEntity!=null){
+            //不存在
+            phoneEntity.setType(1);
+            phoneEntity.setPhone(phone);
+            this.save(phoneEntity);
+        }else {
+            //存在
+            //是否相同
+            if(phone.equals(phoneEntity.getPhone())){
+                return;
+            }
+            phoneEntity.setPhone(phone);
+            this.updateById(phoneEntity);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/RegionServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/RegionServiceImpl.java
new file mode 100644
index 0000000..cd7fc36
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/RegionServiceImpl.java
@@ -0,0 +1,12 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.errand.domain.Region;
+import com.ruoyi.errand.mapper.RegionMapper;
+import com.ruoyi.errand.service.RegionService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> implements RegionService {
+
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/ReportServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/ReportServiceImpl.java
new file mode 100644
index 0000000..e727f82
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/ReportServiceImpl.java
@@ -0,0 +1,67 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.constant.StatusConstant;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.domain.Report;
+import com.ruoyi.errand.mapper.ReportMapper;
+import com.ruoyi.errand.object.dto.app.AddReportDTO;
+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 org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Service
+public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements ReportService {
+
+    @Override
+    public void add(AddReportDTO addReportDTO) {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        Report report = new Report();
+        BeanUtils.copyProperties(addReportDTO,report);
+        report.setAppUserId(appUser.getId());
+        report.setStatus(StatusConstant.UN_DISPOSE);
+        this.save(report);
+    }
+
+    @Override
+    public IPage<ReportPageListVO> getReportList(ReportPageListDTO dto) {
+        IPage<ReportPageListVO> page = new Page<>(dto.getPageNum(),dto.getPageSize());
+        return this.baseMapper.getReportList(page,dto);
+    }
+
+    @Override
+    public void dispose(Integer id) {
+        Report report = this.getById(id);
+        if (report==null || Objects.equals(report.getDelFlag(), DelFlagConstant.DELETE)) {
+            throw new ServiceException("该记录不存在");
+        }
+        if (Objects.equals(report.getStatus(), StatusConstant.DISPOSE)) {
+            throw new ServiceException("该记录已被处理过,请勿重复操作");
+        }
+        report.setStatus(StatusConstant.DISPOSE);
+        report.setUpdateTime(LocalDateTime.now());
+        this.updateById(report);
+    }
+
+    @Override
+    public void delete(Integer id) {
+        Report report = this.getById(id);
+        if (report==null || Objects.equals(report.getDelFlag(), DelFlagConstant.DELETE)) {
+            throw new ServiceException("该记录不存在");
+        }
+        report.setDelFlag(DelFlagConstant.DELETE);
+        report.setUpdateTime(LocalDateTime.now());
+        this.updateById(report);
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/SystemConfigServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/SystemConfigServiceImpl.java
new file mode 100644
index 0000000..4470b80
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/SystemConfigServiceImpl.java
@@ -0,0 +1,12 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.errand.domain.SystemConfig;
+import com.ruoyi.errand.mapper.SystemConfigMapper;
+import com.ruoyi.errand.service.SystemConfigService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SystemConfigServiceImpl extends ServiceImpl<SystemConfigMapper, SystemConfig> implements SystemConfigService {
+
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/UserCancellationLogServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/UserCancellationLogServiceImpl.java
new file mode 100644
index 0000000..b1ce9d5
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/UserCancellationLogServiceImpl.java
@@ -0,0 +1,12 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.errand.domain.UserCancellationLog;
+import com.ruoyi.errand.mapper.UserCancellationLogMapper;
+import com.ruoyi.errand.service.UserCancellationLogService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class UserCancellationLogServiceImpl extends ServiceImpl<UserCancellationLogMapper, UserCancellationLog> implements UserCancellationLogService {
+
+}    
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipOrderServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipOrderServiceImpl.java
new file mode 100644
index 0000000..849a970
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipOrderServiceImpl.java
@@ -0,0 +1,181 @@
+package com.ruoyi.errand.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.errand.constant.DelFlagConstant;
+import com.ruoyi.errand.domain.AppUser;
+import com.ruoyi.errand.domain.VipOrder;
+import com.ruoyi.errand.domain.VipSetting;
+import com.ruoyi.errand.mapper.AppUserMapper;
+import com.ruoyi.errand.mapper.VipOrderMapper;
+import com.ruoyi.errand.mapper.VipSettingMapper;
+import com.ruoyi.errand.object.dto.app.VipPaymentDTO;
+import com.ruoyi.errand.service.VipOrderService;
+import com.ruoyi.errand.utils.CloseOrderResult;
+import com.ruoyi.errand.utils.PaymentUtil;
+import com.ruoyi.errand.utils.UniPayCallbackResult;
+import com.ruoyi.errand.utils.UniPayResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+@Service
+@Slf4j
+public class VipOrderServiceImpl extends ServiceImpl<VipOrderMapper, VipOrder> implements VipOrderService {
+    @Resource
+    private  VipSettingMapper vipSettingMapper;
+    @Resource
+    private RedisTemplate redisTemplate;
+    @Resource
+    private AppUserMapper appUserMapper;
+
+    @Override
+    public R vipPayment(VipPaymentDTO vipPaymentDTO) {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        VipOrder vipOrder = new VipOrder();
+        vipOrder.setAppUserId(appuser.getId());
+
+        VipSetting vipSetting = vipSettingMapper.selectById(vipPaymentDTO.getId());
+        vipOrder.setOrderAmount(vipSetting.getVip_price());
+        vipOrder.setPaymentAmount(vipSetting.getVip_price());
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
+        vipOrder.setOrderNumber("QJS" + getNumber(3) + sdf.format(new Date()));
+        vipOrder.setPayStatus(1);
+        vipOrder.setVipId(vipPaymentDTO.getId());
+        this.save(vipOrder);
+        //判断需要支付的金额是否大于0
+        BigDecimal paymentMoney = vipOrder.getOrderAmount();
+        if ( BigDecimal.ZERO.compareTo(paymentMoney) < 0){
+            //调起微信支付
+            String vipName = vipSetting.getVip_name();
+            UniPayResult uniPayResult = PaymentUtil.uniPay(vipOrder.getOrderNumber(), paymentMoney.doubleValue(),  "购买会员",
+                    vipName, "", "/app/vipOrder/orderPaymentCallback", appuser.getWxOpenid(), null);
+            if(null == uniPayResult || !"100".equals(uniPayResult.getRa_Code())){
+                return R.fail(null == uniPayResult ? "支付失败" : uniPayResult.getRb_CodeMsg());
+            }
+            String rc_result = uniPayResult.getRc_Result();
+            JSONObject jsonObject = JSON.parseObject(rc_result);
+            jsonObject.put("orderId", vipOrder.getId().toString());
+            //将支付数据添加到redis队列中,便于定时任务去校验是否完成支付,没有完成支付支付,15分钟后关闭订单。
+            long second = LocalDateTime.now().plusMinutes(15).toEpochSecond(ZoneOffset.UTC);
+            redisTemplate.opsForZSet().add("VipOrderPayment", vipOrder.getOrderNumber(), second);
+            return R.ok(jsonObject.toJSONString());
+        }
+        //设置vipOrder信息
+        vipOrder.setPayStatus(2);//已支付
+        vipOrder.setOrderTime(LocalDateTime.now());//下单时间
+        this.updateById(vipOrder);
+        //更新用户信息
+        Long appUserId = vipOrder.getAppUserId();
+        AppUser appUser = appUserMapper.selectById(appUserId);
+        appUser.setVipId(vipOrder.getVipId());
+        if (appUser.getEndTime().isBefore(LocalDateTime.now())) {
+            appUser.setStartTime(LocalDateTime.now());
+        }
+        // 根据 vipId 计算会员到期时间
+        LocalDateTime endTime = calculateEndTime(appUser.getEndTime(), vipOrder.getVipId());
+        appUser.setEndTime(endTime);
+        appUserMapper.updateById(appUser);
+        return R.ok();
+
+    }
+
+    @Override
+    public R orderPaymentCallback(UniPayCallbackResult uniPayCallbackResult) {
+        VipOrder vipOrder = this.getBaseMapper().selectOne(new LambdaQueryWrapper<VipOrder>().eq(VipOrder::getOrderNumber, uniPayCallbackResult.getR2_OrderNo()));
+        if(null == vipOrder || vipOrder.getPayStatus() == 2){
+            return R.ok();
+        }
+        //设置vipOrder信息
+        vipOrder.setPayStatus(2);//已支付
+        vipOrder.setOrderTime(LocalDateTime.now());//下单时间
+        String r7TrxNo = uniPayCallbackResult.getR9_BankTrxNo();
+        vipOrder.setSerialNumber(r7TrxNo);
+        this.updateById(vipOrder);
+        //更新用户信息
+        //检查是否续期的
+        Long appUserId = vipOrder.getAppUserId();
+        AppUser appUser = appUserMapper.selectById(appUserId);
+        appUser.setVipId(vipOrder.getVipId());
+        LocalDateTime now = LocalDateTime.now();
+        appUser.setEndTime(appUser.getEndTime()==null?now:appUser.getEndTime());
+        if (appUser.getEndTime().isBefore(now)) {
+            appUser.setStartTime(now);
+        }
+        // 根据 vipId 计算会员到期时间
+        LocalDateTime endTime = calculateEndTime(appUser.getEndTime(), vipOrder.getVipId());
+        appUser.setEndTime(endTime);
+        appUserMapper.updateById(appUser);
+        return R.ok();
+    }
+
+    @Override
+    public void closeOrder() {
+        //订单支付数据
+        long second = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
+        Set<String> orderPayment = redisTemplate.opsForZSet().rangeByScore("VipOrderPayment", 0, second);
+        if(orderPayment.size() > 0){
+            List<VipOrder> list = this.getBaseMapper().selectList(new LambdaQueryWrapper<VipOrder>().in(VipOrder::getOrderNumber, orderPayment));
+            for (VipOrder order : list) {
+                if(null == order || order.getPayStatus() != 1){
+                    //不在待支付中的,移除
+                    redisTemplate.opsForZSet().remove("VipOrderPayment", order.getOrderNumber());
+                    continue;
+                }
+                //开始执行关闭订单操作
+                CloseOrderResult closeOrderResult = PaymentUtil.closeOrder(order.getOrderNumber());
+                if((null == closeOrderResult || !closeOrderResult.getRa_Status().equals("100")) &&
+                        Arrays.asList("0", "4", "101", "10080000", "10080002", "10083004", "10083005").contains(closeOrderResult.getRb_Code())){
+                    redisTemplate.opsForZSet().add("VipOrderPayment", order.getOrderNumber(), 0);
+                    log.error("关闭订单失败:{}---->{}",order.getOrderNumber(), JSON.toJSONString(closeOrderResult));
+                }
+                redisTemplate.opsForZSet().remove("VipOrderPayment", order.getOrderNumber());
+                //删除订单
+                order.setDelFlag(DelFlagConstant.DELETE);
+                this.updateById(order);
+
+            }
+        }
+    }
+
+
+    private LocalDateTime calculateEndTime(LocalDateTime startTime, Integer vipId) {
+        switch (vipId) {
+            case 1: // 月卡
+                return startTime.plusDays(31).withHour(23).withMinute(59).withSecond(59);
+            case 2: // 季卡
+                return startTime.plusDays(90).withHour(23).withMinute(59).withSecond(59);
+            case 3: // 半年卡
+                return startTime.plusDays(180).withHour(23).withMinute(59).withSecond(59);
+            case 4: // 年卡
+                return startTime.plusDays(365).withHour(23).withMinute(59).withSecond(59);
+            default:
+                // 处理无效的 vipId 情况,例如抛出异常或记录日志
+                throw new ServiceException("无效的 vipId: " + vipId);
+        }
+    }
+
+    public String getNumber(Integer size){
+        StringBuilder str = new StringBuilder();
+        for (int i = 0; i < size; i++) {
+            str.append(Double.valueOf(Math.random() * 10).intValue());
+        }
+        return str.toString();
+    }
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipSettingServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipSettingServiceImpl.java
new file mode 100644
index 0000000..7aca788
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/VipSettingServiceImpl.java
@@ -0,0 +1,40 @@
+package com.ruoyi.errand.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.errand.domain.VipSetting;
+import com.ruoyi.errand.mapper.VipSettingMapper;
+import com.ruoyi.errand.object.dto.sys.SetPriceDTO;
+import com.ruoyi.errand.object.vo.app.VipInfoListVO;
+import com.ruoyi.errand.service.VipSettingService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class VipSettingServiceImpl extends ServiceImpl<VipSettingMapper, VipSetting> implements VipSettingService {
+
+    @Override
+    public List<VipInfoListVO> getVipInfoList() {
+        return this.getBaseMapper().getVipInfoList();
+    }
+
+    @Override
+    public void setPrice(SetPriceDTO setPriceDTO) {
+        VipSetting byId = this.getById(setPriceDTO.getId());
+        if (byId == null) {
+            byId=new VipSetting();
+            byId.setId(setPriceDTO.getId());
+            if (setPriceDTO.getId()==1) byId.setVip_name("月卡会员");
+            if (setPriceDTO.getId()==2) byId.setVip_name("季卡会员");
+            if (setPriceDTO.getId()==3) byId.setVip_name("半年卡会员");
+            if (setPriceDTO.getId()==4) byId.setVip_name("年卡会员");
+            byId.setVip_price(setPriceDTO.getVip_price());
+            this.save(byId);
+        }else {
+            byId.setVip_price(setPriceDTO.getVip_price());
+            this.updateById(byId);
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/AES.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/AES.java
new file mode 100644
index 0000000..b18277c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/AES.java
@@ -0,0 +1,72 @@
+package com.ruoyi.errand.utils;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.*;
+
+/**
+* AES加密
+* @author pzb
+* @Date 2021/12/3 15:43
+*/
+public class AES {
+
+    public static boolean initialized = false;
+
+    /**
+     * AES解密
+     *
+     * @param content
+     *            密文
+     * @return
+     * @throws InvalidAlgorithmParameterException
+     * @throws NoSuchProviderException
+     */
+    public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
+        initialize();
+        try {
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
+            Key sKeySpec = new SecretKeySpec(keyByte, "AES");
+            cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
+            byte[] result = cipher.doFinal(content);
+            return result;
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (NoSuchPaddingException e) {
+            e.printStackTrace();
+        } catch (InvalidKeyException e) {
+            e.printStackTrace();
+        } catch (IllegalBlockSizeException e) {
+            e.printStackTrace();
+        } catch (BadPaddingException e) {
+            e.printStackTrace();
+        } catch (NoSuchProviderException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static void initialize() {
+        if (initialized)
+            return;
+        Security.addProvider(new BouncyCastleProvider());
+        initialized = true;
+    }
+
+    // 生成iv
+    public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+        params.init(new IvParameterSpec(iv));
+        return params;
+    }
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/BatchNumberUtils.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/BatchNumberUtils.java
new file mode 100644
index 0000000..3b03656
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/BatchNumberUtils.java
@@ -0,0 +1,120 @@
+package com.ruoyi.errand.utils;
+
+import cn.afterturn.easypoi.excel.ExcelExportUtil;
+import cn.afterturn.easypoi.excel.entity.ExportParams;
+import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
+import com.ruoyi.common.exception.GlobalException;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class BatchNumberUtils {
+
+    private static final String PREFIX = "第";
+    private static final String SUFFIX = "批";
+
+    /**
+     * 生成批次信息
+     *
+     * @param lastBatchNumber
+     * @return
+     */
+    public static String getBatchNumber(String lastBatchNumber) {
+        if (lastBatchNumber == null || !lastBatchNumber.startsWith(PREFIX) || !lastBatchNumber.endsWith(SUFFIX)) {
+            return PREFIX + "1" + SUFFIX;
+        }
+        try {
+            // 提取中间的数字
+            String numberPart = lastBatchNumber.substring(PREFIX.length(), lastBatchNumber.length() - SUFFIX.length());
+            int batchNumber = Integer.parseInt(numberPart);
+
+            if (batchNumber < 1 || batchNumber >= 9999) {
+                throw new GlobalException("批次数必须在 1 到 9999 之间");
+            }
+
+            // 计算下一个批次
+            return PREFIX + (batchNumber + 1) + SUFFIX;
+        } catch (NumberFormatException e) {
+            throw new GlobalException("批次中的数字解析失败,请确保格式正确");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        // 1. 创建导出参数
+        ExportParams exportParams = new ExportParams("动态表头示例", "Sheet1");
+
+        // 2. 创建表头
+        List<ExcelExportEntity> columnList = new ArrayList<>();
+
+        // 3. 添加静态列
+        columnList.add(new ExcelExportEntity("编号", "id"));
+        columnList.add(new ExcelExportEntity("补偿资金总额(万元)", "totalCompensation"));
+        columnList.add(new ExcelExportEntity("25%首付款", "firstPayment"));
+        columnList.add(new ExcelExportEntity("过渡补贴", "transitionSubsidy"));
+
+        // 4. 创建父级列 "每季度需支付款项(万元)",设置 key 为 "dynamic"
+        ExcelExportEntity parentColumn = new ExcelExportEntity("每季度需支付款项(万元)", "dynamic");
+        List<ExcelExportEntity> dynamicColumns = new ArrayList<>();
+
+        // 5. 生成动态列(2025-2027,每年4个季度)
+        List<String> dynamicHeaders = generateDynamicHeaders(2025, 2027);
+        for (String header : dynamicHeaders) {
+            ExcelExportEntity column = new ExcelExportEntity(header, header);
+            column.setFormat("0.00"); // 确保Excel数值格式正确
+            dynamicColumns.add(column);
+        }
+
+        // 6. 关联子列
+        parentColumn.setList(dynamicColumns);
+        columnList.add(parentColumn);
+
+        // 7. 组织数据
+        List<Map<String, Object>> dataList = new ArrayList<>();
+        for (int i = 1; i <= 5; i++) { // 生成5行数据
+            Map<String, Object> row = new HashMap<>();
+            row.put("id", i);
+            row.put("totalCompensation", 500 + i * 10);
+            row.put("firstPayment", 120 + i * 5);
+            row.put("transitionSubsidy", 30 + i * 2);
+
+            // 新建子Map存放动态列数据
+            Map<String, Object> dynamicData = new HashMap<>();
+            for (String header : dynamicHeaders) {
+                dynamicData.put(header, Math.round(Math.random() * 100 * 100.0) / 100.0); // 存储 Double 类型数据
+            }
+            // 将子Map以 key "dynamic" 存入 row 中
+            row.put("dynamic", dynamicData);
+
+            System.out.println("生成数据:" + row); // 调试输出
+            dataList.add(row);
+        }
+
+        // 8. 导出 Excel
+        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columnList, dataList);
+
+        // 9. 写入文件
+        try (FileOutputStream fos = new FileOutputStream("DynamicTable.xlsx")) {
+            workbook.write(fos);
+        }
+        workbook.close();
+
+        System.out.println("Excel 导出成功!");
+    }
+
+    // 生成动态季度时间列
+    public static List<String> generateDynamicHeaders(int startYear, int endYear) {
+        List<String> headers = new ArrayList<>();
+        String[] quarters = {"1月", "4月", "7月", "10月"};
+        for (int year = startYear; year <= endYear; year++) {
+            for (String quarter : quarters) {
+                headers.add(year + "年" + quarter);
+            }
+        }
+        return headers;
+    }
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/CloseOrderResult.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/CloseOrderResult.java
new file mode 100644
index 0000000..e40824a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/CloseOrderResult.java
@@ -0,0 +1,41 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+
+@Data
+public class CloseOrderResult {
+	/**
+	 * 商户编号
+	 */
+	private String r1_MerchantNo;
+	/**
+	 * 业务结果 100:成功,101:失败
+	 */
+	private String ra_Status;
+	/**
+	 * 响应码
+	 * 0 系统连接超时
+	 * 4 服务不可用
+	 * 100 关单成功
+	 * 101 失败,详见响应码描述
+	 * 10080000 系统异常
+	 * 10080002 验证签名失败
+	 * 10080003 订单号不正确
+	 * 10080042 交易类型不合法
+	 * 10083001 订单正在处理中
+	 * 10083002 该订单请求多次交易
+	 * 10083003 订单已关闭,无需关单操作
+	 * 10083003 交易成功,无需关单操作
+	 * 10083004 通道系统异常,请用相同参数重新请求
+	 * 10083005 通道其他异常信息
+	 */
+	private String rb_Code;
+	/**
+	 * 响应码描述
+	 */
+	private String rc_CodeMsg;
+	/**
+	 * 签名数据
+	 */
+	private String hmac;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java
new file mode 100644
index 0000000..9badea6
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java
@@ -0,0 +1,115 @@
+package com.ruoyi.errand.utils;
+
+
+import io.jsonwebtoken.Claims;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.websocket.CloseReason;
+import javax.websocket.OnClose;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+@ServerEndpoint("/ws/delivery")
+@Component
+public class DeliveryWebSocket {
+
+    private static ConcurrentHashMap<String, Session> deliveryPersonSessions = new ConcurrentHashMap<>();
+
+
+    private static  RedisTemplate redisTemplate;
+    @Autowired
+    public void setRedisTemplate(RedisTemplate redisTemplate) {
+        DeliveryWebSocket.redisTemplate = redisTemplate;
+    }
+
+
+    @OnOpen
+    public void onOpen(Session session) {
+        // 从查询参数获取token
+        String token = session.getRequestParameterMap().get("token").stream().findFirst().orElse(null);
+        // 验证token
+        String userId=validateToken(token);
+        if (null==userId) {
+            try {
+                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Invalid token"));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            return;
+        }
+
+        deliveryPersonSessions.put(userId, session);
+        checkPendingNotifications(userId);
+    }
+
+    private String validateToken(String token) {
+        if (token == null || !token.startsWith("app:")) {
+            return null;
+        }
+        try {
+            //解析token 获取userid,再查询到AppUser
+            String realToken = token.substring(4);
+            Claims claims = JwtUtil.parseJWT(realToken);
+            String userId = claims.get("userId").toString();
+            return userId;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    @OnClose
+    public void onClose(Session session) {
+        String token = session.getRequestParameterMap().get("token").stream().findFirst().orElse(null);
+        // 验证token
+        String userId=validateToken(token);
+        if (null==userId) {
+            try {
+                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Invalid token"));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            return;
+        }
+        deliveryPersonSessions.remove(userId);
+    }
+
+    private void checkPendingNotifications(String userId) {
+        String key = "delivery:notification:" + userId;
+        List<Object> notifications = redisTemplate.opsForList().range(key, 0, -1);
+        if (notifications != null && !notifications.isEmpty()) {
+            for (Object notification : notifications) {
+                sendNotification(userId, notification.toString());
+            }
+            redisTemplate.delete(key);
+        }
+    }
+
+    public  void sendNotification(String userId, String message) {
+        Session session = deliveryPersonSessions.get(userId);
+        if (session != null && session.isOpen()) {
+            try {
+                session.getBasicRemote().sendText(message);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            // 用户不在线,存入Redis
+            String key = "delivery:notification:" + userId;
+            redisTemplate.opsForList().rightPush(key, message);
+            // 设置过期时间,比如1天
+            redisTemplate.expire(key, 1, TimeUnit.DAYS);
+        }
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/FrpCodeEnum.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/FrpCodeEnum.java
new file mode 100644
index 0000000..987c2d1
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/FrpCodeEnum.java
@@ -0,0 +1,52 @@
+package com.ruoyi.errand.utils;
+
+public enum FrpCodeEnum {
+	ALIPAY_NATIVE("支付宝扫码(主扫)", "ALIPAY_NATIVE"),
+	ALIPAY_CARD("支付宝刷卡(被扫)", "ALIPAY_CARD"),
+	ALIPAY_H5("支付宝 H5", "ALIPAY_H5"),
+	ALIPAY_FWC("支付宝服务窗", "ALIPAY_FWC"),
+	ALIPAY_SYT("支付宝收银台", "ALIPAY_SYT"),
+	WEIXIN_NATIVE("微信扫码(主扫)", "WEIXIN_NATIVE"),
+	WEIXIN_CARD("微信刷卡(被扫)", "WEIXIN_CARD"),
+	WEIXIN_APP3("微信 APP+支付", "WEIXIN_APP3"),
+	WEIXIN_H5_PLUS("微信 H5 支付", "WEIXIN_H5_PLUS"),
+	WEIXIN_GZH("微信公众号支付", "WEIXIN_GZH"),
+	WEIXIN_XCX("微信小程序支付", "WEIXIN_XCX"),
+	QQ_NATIVE("QQ 扫码(主扫)", "QQ_NATIVE"),
+	QQ_CARD("QQ 刷卡(被扫)", "QQ_CARD"),
+	QQ_APP("QQ APP 支付", "QQ_APP"),
+	QQ_H5("QQH5 支付", "QQ_H5"),
+	QQ_GZH("QQ 公众号支付", "QQ_GZH"),
+	UNIONPAY_NATIVE("银联扫码(主扫)", "UNIONPAY_NATIVE"),
+	UNIONPAY_CARD("银联刷卡(被扫)", "UNIONPAY_CARD"),
+	UNIONPAY_APP("银联 APP 支付", "UNIONPAY_APP"),
+	UNIONPAY_H5("银联 H5", "UNIONPAY_H5"),
+	UNIONPAY_SYT("银联统一收银台", "UNIONPAY_SYT"),
+	UNIONPAY_WXMP("银联云微小程序(无感支付)", "UNIONPAY_WXMP")
+	;
+	
+	private String name;
+	
+	private String code;
+	
+	FrpCodeEnum(String name, String code) {
+		this.name = name;
+		this.code = code;
+	}
+	
+	public String getName() {
+		return name;
+	}
+	
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	public String getCode() {
+		return code;
+	}
+	
+	public void setCode(String code) {
+		this.code = code;
+	}
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/JwtUtil.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/JwtUtil.java
new file mode 100644
index 0000000..4590c8a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/JwtUtil.java
@@ -0,0 +1,71 @@
+package com.ruoyi.errand.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.springframework.stereotype.Component;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class JwtUtil {
+
+    /**
+     * 用户生成jwt令牌相关配置
+     */
+    private static final String secretKey = "xx";
+    private static final long ttl =7200000;
+    private static final String tokenName = "Authorization";
+
+    /**
+     * 生成jwt
+     * 使用Hs256算法, 私匙使用固定秘钥
+     * @param claims    设置的信息
+     * @return
+     */
+
+    public static Map<String, Object> createJWT( Map<String, Object> claims) {
+        Map<String, Object> jwtMap = new HashMap<>();
+        // 指定签名的时候使用的签名算法,也就是header那部分
+        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
+        // 生成JWT的时间
+        long expMillis = System.currentTimeMillis() + ttl;
+        Date exp = new Date(expMillis);
+
+        // 设置jwt的body
+        JwtBuilder builder = Jwts.builder()
+                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
+                .setClaims(claims)
+                // 设置签名使用的签名算法和签名使用的秘钥
+                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
+                // 设置过期时间
+                .setExpiration(exp);
+        jwtMap.put("token", builder.compact());
+        jwtMap.put("exp", expMillis);
+        return jwtMap;
+    }
+
+    /**
+     * Token解密
+     * @param token     加密后的token
+     * @return
+     */
+    public static Claims parseJWT( String token) {
+        // 得到DefaultJwtParser
+        Claims claims = Jwts.parser()
+                // 设置签名的秘钥
+                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
+                // 设置需要解析的jwt
+                .parseClaimsJws(token).getBody();
+        return claims;
+    }
+
+    public String getTokenName() {
+        return tokenName;
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/MD5AndKL.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/MD5AndKL.java
new file mode 100644
index 0000000..0b82932
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/MD5AndKL.java
@@ -0,0 +1,112 @@
+package com.ruoyi.errand.utils;
+
+import java.security.MessageDigest;
+
+public class MD5AndKL {
+	
+	/**
+	 * MD5加码。32位
+	 * 
+	 * @param inStr
+	 * @return
+	 */
+	public static String MD5(String inStr) {
+		MessageDigest md5 = null;
+		try {
+			md5 = MessageDigest.getInstance("MD5");
+		} catch (Exception e) {
+			throw new RuntimeException(e.toString());
+		}
+		byte[] md5Bytes = md5.digest(inStr.getBytes());
+		StringBuffer hexValue = new StringBuffer();
+		for (int i = 0; i < md5Bytes.length; i++) {
+			int val = ((int) md5Bytes[i]) & 0xff;
+			if (val < 16) {
+				hexValue.append("0");
+			}
+			hexValue.append(Integer.toHexString(val));
+		}
+		return hexValue.toString();
+	}
+
+	/**
+	 * 可逆的加密算法
+	 * 
+	 * @param inStr
+	 * @return
+	 */
+	public static String KL(String inStr) {
+		char[] a = inStr.toCharArray();
+		for (int i = 0; i < a.length; i++) {
+			a[i] = (char) (a[i] ^ 't');
+		}
+		String s = new String(a);
+		return s;
+	}
+
+	/**
+	 * 加密后解密
+	 * 
+	 * @param inStr
+	 * @return
+	 */
+	public static String JM(String inStr) {
+		char[] a = inStr.toCharArray();
+		for (int i = 0; i < a.length; i++) {
+			a[i] = (char) (a[i] ^ 't');
+		}
+		String k = new String(a);
+		return k;
+	}
+
+
+
+	private static String byteArrayToHexString(byte b[]) {
+		StringBuffer resultSb = new StringBuffer();
+		for (int i = 0; i < b.length; i++)
+			resultSb.append(byteToHexString(b[i]));
+
+		return resultSb.toString();
+	}
+
+	private static String byteToHexString(byte b) {
+		int n = b;
+		if (n < 0)
+			n += 256;
+		int d1 = n / 16;
+		int d2 = n % 16;
+		return hexDigits[d1] + hexDigits[d2];
+	}
+
+	public static String MD5Encode(String origin, String charsetname) {
+		String resultString = null;
+		try {
+			resultString = new String(origin);
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			if (charsetname == null || "".equals(charsetname)){
+				resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
+			}else{
+				resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
+			}
+		} catch (Exception exception) {
+			exception.printStackTrace();
+		}
+		return resultString;
+	}
+
+	private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
+			"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
+
+	public static void main(String args[]) {
+		 
+		System.out.println("MD5后再加密:" + KL(MD5("123456")));
+		System.out.println(MD5("123456"));
+		// System.out.println("加密:" + KL(MD5("123456")));
+		// s = KL(s);
+		// System.out.println("解密:" + KL("81dc9bdb52d04dc20036dbd8313ed055"));
+		// System.out.println("解密:" + JM(KL(s)));
+		// System.out.println("解密为MD5后的:" + KL(KL(MD5(s))));
+		// System.out.println(JM("5d62957bb57d3e49dcf48a0df064be4c"));
+		// System.out.println(MD5AndKL.KL(MD5AndKL.MD5("admin"+"87654321")));
+	}
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentCycleHelper.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentCycleHelper.java
new file mode 100644
index 0000000..9e25b86
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentCycleHelper.java
@@ -0,0 +1,200 @@
+package com.ruoyi.errand.utils;
+
+import cn.hutool.core.date.DateUtil;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+public class PaymentCycleHelper {
+
+    // 格式化日期为 "yyyy-MM"
+    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
+
+    // 根据 25% 首付款支付时间计算首笔付款周期
+    public static String getFirstPaymentCycle(Date firstPaymentDate) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(firstPaymentDate);
+
+        int month = calendar.get(Calendar.MONTH) + 1; // 获取月份(1-12)
+
+        if (month >= 10 && month <= 12) {
+            // 10月-12月,首笔付款周期为次年1月
+            calendar.add(Calendar.YEAR, 1);
+            calendar.set(Calendar.MONTH, Calendar.JANUARY); // 设置为1月
+        } else if (month >= 1 && month <= 3) {
+            // 1月-3月,首笔付款周期为当年4月
+            calendar.set(Calendar.MONTH, Calendar.APRIL);
+        } else if (month >= 4 && month <= 6) {
+            // 4月-6月,首笔付款周期为当年7月
+            calendar.set(Calendar.MONTH, Calendar.JULY);
+        } else if (month >= 7 && month <= 9) {
+            // 7月-9月,首笔付款周期为当年10月
+            calendar.set(Calendar.MONTH, Calendar.OCTOBER);
+        }
+
+        // 返回首笔付款周期格式 "yyyy-MM"
+        return sdf.format(calendar.getTime());
+    }
+
+    public static String getCurrentQuarter(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+
+        int year = calendar.get(Calendar.YEAR);
+        int month = calendar.get(Calendar.MONTH) + 1; // 获取月份(1-12)
+
+        // 计算当前季度的起始月份
+        int quarterStartMonth = ((month - 1) / 3) * 3 + 1;
+
+        return String.format("%d-%02d", year, quarterStartMonth);
+    }
+
+    /**
+     * 获取季度信息
+     * @param date
+     * @param offset
+     * @return
+     */
+    public static String getQuarterByOffset(Date date, int offset) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+
+        int year = calendar.get(Calendar.YEAR);
+        int month = calendar.get(Calendar.MONTH) + 1; // Java中月份是从0开始的
+
+        // 当前季度索引:0(Q1),1(Q2),2(Q3),3(Q4)
+        int currentQuarter = (month - 1) / 3;
+
+        // 计算目标季度的索引
+        int targetQuarter = currentQuarter + offset;
+
+        // 处理跨年情况
+        year += targetQuarter / 4;
+        targetQuarter = (targetQuarter % 4 + 4) % 4; // 保证结果在0~3之间
+
+        // 获取目标季度的起始月份
+        int quarterStartMonth = targetQuarter * 3 + 1;
+
+        return String.format("%d-%02d", year, quarterStartMonth);
+    }
+
+    // 根据首笔付款日期往后推 20 期,计算分期支付周期
+    public static String getQuarterlyPaymentCycles(Date firstPaymentDate) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(firstPaymentDate);
+
+        // 获取首笔付款周期
+        String firstPaymentCycle = getFirstPaymentCycle(firstPaymentDate);
+        try {
+            calendar.setTime(sdf.parse(firstPaymentCycle));  // 将首笔付款周期解析为日期
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "";
+        }
+
+        // 往后推 20 期,每期为3个月
+        String[] paymentCycles = new String[20];
+        for (int i = 0; i < 20; i++) {
+            paymentCycles[i] = sdf.format(calendar.getTime());
+            calendar.add(Calendar.MONTH, 3);  // 每次增加 3 个月
+        }
+
+        return String.join(",", Arrays.asList(paymentCycles));
+    }
+
+    // 根据当前季度首笔付款日期往后推 20 期,计算分期支付周期
+    public static List<String> getQuarterlyPaymentCyclesList(Date firstPaymentDate) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(firstPaymentDate);
+
+        // 获取首笔付款周期
+        String firstPaymentCycle = getCurrentQuarter(firstPaymentDate);
+        try {
+            calendar.setTime(sdf.parse(firstPaymentCycle));  // 将首笔付款周期解析为日期
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        // 往后推 20 期,每期为3个月
+        String[] paymentCycles = new String[20];
+        for (int i = 0; i < 20; i++) {
+            paymentCycles[i] = sdf.format(calendar.getTime());
+            calendar.add(Calendar.MONTH, 3);  // 每次增加 3 个月
+        }
+        return Arrays.asList(paymentCycles);
+    }
+
+
+    // 根据首笔付款日期往后推 4 期,计算分期支付周期
+    public static List<String> getQuarterlyPaymentCyclesFour(Date firstPaymentDate) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(firstPaymentDate);
+
+        // 获取首笔付款周期
+        String firstPaymentCycle = getFirstPaymentCycle(firstPaymentDate);
+        try {
+            calendar.setTime(sdf.parse(firstPaymentCycle));  // 将首笔付款周期解析为日期
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+
+        // 往后推 20 期,每期为3个月
+        String[] paymentCycles = new String[4];
+        for (int i = 0; i < 4; i++) {
+            paymentCycles[i] = sdf.format(calendar.getTime());
+            calendar.add(Calendar.MONTH, 3);  // 每次增加 3 个月
+        }
+        return Arrays.asList(paymentCycles);
+    }
+
+    /**
+     * 计算 25% 首付款支付时间属于 20 期中的第几期
+     *
+     * @param firstPaymentDate 25% 首付款支付时间(Date 类型)
+     * @return 期数(1~20),超出则返回 -1
+     */
+    public static int getPaymentPeriod(Date firstPaymentDate) {
+        //判断时间超过5年,则直接返回20
+        long month = DateUtil.betweenMonth(new Date(),firstPaymentDate,true);
+        if(month >= 60){
+            return 20;
+        }
+        List<String> times = getQuarterlyPaymentCyclesList(firstPaymentDate);
+
+        //获取当前日期季度
+        String current = getCurrentQuarter(new Date());
+
+        // 计算当前属于哪一期
+        for (int i = 0; i <= times.size(); i++) {
+            String itemTime = times.get(i);
+            if (itemTime.equals(current)) {
+                return i;
+            }
+        }
+        // 如果当前日期超出 20 期,则返回 -1
+        return 0;
+    }
+
+    /**
+     * 将 Date 转换为 LocalDate
+     *
+     * @param date Date 类型的日期
+     * @return LocalDate 类型的日期
+     */
+    private static LocalDate convertToLocalDate(Date date) {
+        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
+
+    public static void main(String[] args) {
+        System.out.println(getPaymentPeriod(DateUtil.parseDate(" 2023-10-03")));;
+    }
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentUtil.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentUtil.java
new file mode 100644
index 0000000..46bbf3a
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/PaymentUtil.java
@@ -0,0 +1,285 @@
+package com.ruoyi.errand.utils;
+
+import cn.hutool.http.*;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+
+
+import com.ruoyi.common.utils.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.*;
+
+@Slf4j
+public class PaymentUtil {
+	
+	//微信公众号、微信小程序、微信 APP+/H5、云微小程序支付
+	private static final String appId = "wxdeed472c98e42a54";
+	/**
+	 * 商户密钥
+	 */
+	private static final String key = "925899fcc374430f9e4b4ba3db05b448";
+	/**
+	 * 商户号
+	 */
+	private static final String merchantNo = "888122600004175";
+	/**
+	 * 平台-报备商户号
+	 */
+	private static final String sysTradeMerchantNo = "777168500885852";
+	/**
+	 * 支付回调地址
+	 */
+	private static final String callbackUrl = "https://www.qijisheng.top";
+	
+	
+	/**
+	 * 支付
+	 * @param orderNo           商户订单号
+	 * @param amount            订单金额
+	 * @param productName       商品名称
+	 * @param productDesc       商品描述
+	 * @param mp                公用回传参数
+	 * @param notifyUrl         服务器异步通知地址
+	 * @param openId            微信 Openid
+	 * @param tradeMerchantNo   报备商户号
+	 * @return
+	 */
+	public static UniPayResult uniPay(String orderNo, Double amount, String productName, String productDesc, String mp, String notifyUrl, String openId, String tradeMerchantNo){
+		String url = "https://trade.joinpay.com/tradeRt/uniPay";
+		HttpRequest post = HttpUtil.createPost(url);
+		post.header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded");
+		JSONObject body = new JSONObject();
+		//版本号
+		body.put("p0_Version", "2.5");
+		//商户编号
+		body.put("p1_MerchantNo", merchantNo);
+		//商户订单号
+		body.put("p2_OrderNo", orderNo);
+		//订单金额
+		body.put("p3_Amount", amount);
+		//交易币种
+		body.put("p4_Cur", "1");
+		//商品名称
+		body.put("p5_ProductName", productName);
+		//商品描述
+		body.put("p6_ProductDesc", productDesc);
+		//公用回传参数
+		body.put("p7_Mp", mp);
+		//服务器异步通知地址
+		body.put("p9_NotifyUrl", callbackUrl + notifyUrl);
+		//交易类型
+		body.put("q1_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode());
+		//微信 Openid
+		body.put("q5_OpenId", openId);
+		//APPID
+		body.put("q7_AppId", appId);
+		//报备商户号
+		body.put("qa_TradeMerchantNo", StringUtils.isNotEmpty(tradeMerchantNo) ? tradeMerchantNo : sysTradeMerchantNo);
+		String sign = null;
+		try {
+			sign = sign(body);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+		body.put("hmac", sign);
+		post.form(body);
+		log.info("支付接口请求参数:" + body);
+		HttpResponse execute = post.execute();
+		log.info("支付接口请求响应:" + execute.body());
+		if(200 != execute.getStatus()){
+			log.error("支付接口异常:" + execute.body());
+			return null;
+		}
+		UniPayResult uniPayResult = JSON.parseObject(execute.body(), UniPayResult.class);
+		return uniPayResult;
+	}
+	
+	
+	/**
+	 * 查询支付订单
+	 * @param orderNo   订单号
+	 * @return
+	 */
+	public static QueryOrderResult queryOrder(String orderNo){
+		String url = "https://trade.joinpay.com/tradeRt/queryOrder";
+		HttpRequest post = HttpUtil.createPost(url);
+		post.header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded");
+		JSONObject body = new JSONObject();
+		//版本号
+		body.put("p0_Version", "2.5");
+		//商户编号
+		body.put("p1_MerchantNo", merchantNo);
+		//商户订单号
+		body.put("p2_OrderNo", orderNo);
+		String sign = null;
+		try {
+			sign = sign(body);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+		body.put("hmac", sign);
+		post.form(body);
+		log.info("查询支付接口请求参数:" + body);
+		HttpResponse execute = post.execute();
+		log.info("查询支付接口请求响应:" + execute.body());
+		if(200 != execute.getStatus()){
+			log.error("查询支付接口异常:" + execute.body());
+			return null;
+		}
+		QueryOrderResult uniPayResult = JSON.parseObject(execute.body(), QueryOrderResult.class);
+		return uniPayResult;
+	}
+	
+	
+	/**
+	 * 退款
+	 * @param orderNo           支付订单号
+	 * @param refundOrderNo     退款订单号
+	 * @param refundAmount      退款金额
+	 * @param notifyUrl         异步通知地址
+	 * @return
+	 */
+	public static RefundResult refund(String orderNo, String refundOrderNo, Double refundAmount, String notifyUrl){
+		String url = "https://trade.joinpay.com/tradeRt/refund";
+		HttpRequest post = HttpUtil.createPost(url);
+		post.header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded");
+		JSONObject body = new JSONObject();
+		//版本号
+		body.put("p0_Version", "2.3");
+		//商户编号
+		body.put("p1_MerchantNo", merchantNo);
+		//商户订单号
+		body.put("p2_OrderNo", orderNo);
+		//商户退款订单号
+		body.put("p3_RefundOrderNo", refundOrderNo);
+		//退款金额
+		body.put("p4_RefundAmount", refundAmount);
+		//服务器异步通知地址
+		body.put("p6_NotifyUrl", callbackUrl + notifyUrl);
+		String sign = null;
+		try {
+			sign = sign(body);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+		body.put("hmac", sign);
+		post.form(body);
+		log.info("退款接口请求参数:" + body);
+		HttpResponse execute = post.execute();
+		log.info("退款接口请求响应:" + execute.body());
+		if(200 != execute.getStatus()){
+			log.error("退款接口异常:" + execute.body());
+			return null;
+		}
+		RefundResult uniPayResult = JSON.parseObject(execute.body(), RefundResult.class);
+		return uniPayResult;
+	}
+	
+	
+	/**
+	 * 查询退款订单
+	 * @param refundOrderNo 退款订单号
+	 * @return
+	 */
+	public static QueryRefundResult queryRefund(String refundOrderNo){
+		String url = "https://trade.joinpay.com/tradeRt/refund";
+		HttpRequest post = HttpUtil.createPost(url);
+		post.header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded");
+		JSONObject body = new JSONObject();
+		//版本号
+		body.put("p0_Version", "2.3");
+		//商户编号
+		body.put("p1_MerchantNo", merchantNo);
+		//商户退款订单号
+		body.put("p2_RefundOrderNo", refundOrderNo);
+		String sign = null;
+		try {
+			sign = sign(body);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+		body.put("hmac", sign);
+		post.form(body);
+		log.info("退款接口请求参数:" + body);
+		HttpResponse execute = post.execute();
+		log.info("退款接口请求响应:" + execute.body());
+		if(200 != execute.getStatus()){
+			log.error("退款接口异常:" + execute.body());
+			return null;
+		}
+		QueryRefundResult uniPayResult = JSON.parseObject(execute.body(), QueryRefundResult.class);
+		return uniPayResult;
+	}
+	
+	
+	/**
+	 * 关闭订单(仅支持微信和支付宝)
+	 * @param orderNo   订单号
+	 * @return
+	 */
+	public static CloseOrderResult closeOrder(String orderNo){
+		String url = "https://www.joinpay.com/trade/closeOrder.action";
+		HttpRequest post = HttpUtil.createPost(url);
+		post.header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded");
+		JSONObject body = new JSONObject();
+		//商户编号
+		body.put("p1_MerchantNo", merchantNo);
+		//商户订单号
+		body.put("p2_OrderNo", orderNo);
+		//交易类型
+		body.put("p3_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode());
+		String sign = null;
+		try {
+			sign = sign(body);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+		body.put("hmac", sign);
+		post.form(body);
+		log.info("关闭订单接口请求参数:" + body);
+		HttpResponse execute = post.execute();
+		log.info("关闭订单接口请求响应:" + execute.body());
+		if(200 != execute.getStatus()){
+			log.error("关闭订单接口异常:" + execute.body());
+			return null;
+		}
+		CloseOrderResult uniPayResult = JSON.parseObject(execute.body(), CloseOrderResult.class);
+		return uniPayResult;
+	}
+	
+	
+	
+	public static String sign(JSONObject body) {
+		Set<Map.Entry<String, Object>> entries = body.entrySet();
+		List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries);
+		// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
+		Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
+			public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
+				return (o1.getKey()).compareTo(o2.getKey());
+			}
+		});
+		// 构造签名键值对的格式
+		StringBuilder sb = new StringBuilder();
+		for (Map.Entry<String, Object> item : infoIds) {
+			if (item.getKey() != null || item.getKey() != "") {
+				Object val = item.getValue();
+				if (!(val == "" || val == null)) {
+					sb.append(val);
+				}
+			}
+		}
+		sb.append(key);
+		log.info("待签名串:{}", sb.toString());
+		return MD5AndKL.MD5(sb.toString());
+	}
+	
+	
+	public static void main(String[] args) {
+//		UniPayResult uniPayResult = PaymentUtil.uniPay("852963742", 0.01D, "测试商品", "这是用于对接支付测试的商品描述",
+//				"", "/order/shopping-cart/shoppingCartPaymentCallback", "ooOrs64zHLuInkZ_GF0LpIN9_Rxc", "777168500885852");
+//		PaymentUtil.queryOrder("852963742");
+//		PaymentUtil.closeOrder("852963742");
+		
+	}
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/QueryOrderResult.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/QueryOrderResult.java
new file mode 100644
index 0000000..a714551
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/QueryOrderResult.java
@@ -0,0 +1,83 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+
+@Data
+public class QueryOrderResult {
+	/**
+	 * 版本号
+	 */
+	private Double r0_Version;
+	/**
+	 * 商户编号
+	 */
+	private String r1_MerchantNo;
+	/**
+	 * 商户订单号
+	 */
+	private String r2_OrderNo;
+	/**
+	 * 支付金额
+	 */
+	private Double r3_Amount;
+	/**
+	 * 商品名称
+	 */
+	private String r4_ProductName;
+	/**
+	 * 交易流水号
+	 */
+	private String r5_TrxNo;
+	/**
+	 * 银行流水号
+	 */
+	private String r6_BankTrxNo;
+	/**
+	 * 订单手续费
+	 */
+	private Double r7_Fee;
+	/**
+	 * 交易类型
+	 */
+	private String r8_FrpCode;
+	/**
+	 * 订单状态 100:成功,101:失败,102:已创建,105:订单已关闭
+	 */
+	private String ra_Status;
+	/**
+	 * 响应码
+	 */
+	private String rb_Code;
+	/**
+	 * 响应码描述
+	 */
+	private String rc_CodeMsg;
+	/**
+	 * 用户标识
+	 */
+	private String rd_OpenId;
+	/**
+	 * 平台优惠金额
+	 */
+	private Double re_DiscountAmount;
+	/**
+	 * 支付时间
+	 */
+	private String rf_PayTime;
+	/**
+	 * 卡类型
+	 */
+	private String rh_cardType;
+	/**
+	 * 银行编码
+	 */
+	private String rj_BankCode;
+	/**
+	 * 签约 ID
+	 */
+	private String rl_ContractId;
+	/**
+	 * 签名数据
+	 */
+	private String hmac;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/QueryRefundResult.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/QueryRefundResult.java
new file mode 100644
index 0000000..00b0809
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/QueryRefundResult.java
@@ -0,0 +1,58 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+
+@Data
+public class QueryRefundResult {
+	/**
+	 * 版本号
+	 */
+	private Double r0_Version;
+	/**
+	 * 商户编号
+	 */
+	private String r1_MerchantNo;
+	/**
+	 * 商户退款订单号
+	 */
+	private String r2_RefundOrderNo;
+	/**
+	 * 退款金额
+	 */
+	private Double r3_RefundAmount;
+	/**
+	 * 退款流水号
+	 */
+	private String r4_RefundTrxNo;
+	/**
+	 * 退款完成时间
+	 */
+	private String r5_RefundCompleteTime;
+	/**
+	 * 退款渠道
+	 */
+	private String r8_RefundWay;
+	/**
+	 * 退款入账账户
+	 */
+	private String r9_ReceiveAccountNo;
+	/**
+	 * 退款状态
+	 * 100:退款成功
+	 * 101:退款失败
+	 * 102:退款处理中
+	 */
+	private String ra_Status;
+	/**
+	 * 响应码
+	 */
+	private String rb_Code;
+	/**
+	 * 响应码描述
+	 */
+	private String rc_CodeMsg;
+	/**
+	 * 签名数据
+	 */
+	private String hmac;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/RedisService.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/RedisService.java
new file mode 100644
index 0000000..be9d856
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/RedisService.java
@@ -0,0 +1,276 @@
+package com.ruoyi.errand.utils;
+
+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 RedisService
+{
+    @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 Long 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);
+        }
+    }
+
+    public <T> void setCacheMap(final String key, final Map<String, T> dataMap, long timeout)
+    {
+        if (dataMap != null) {
+            redisTemplate.opsForHash().putAll(key, dataMap);
+            redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
+        }
+    }
+
+    /**
+     * 获得缓存的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);
+    }
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/RefundCallbackResult.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/RefundCallbackResult.java
new file mode 100644
index 0000000..d4d0792
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/RefundCallbackResult.java
@@ -0,0 +1,59 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+
+@Data
+public class RefundCallbackResult {
+	/**
+	 * 版本号
+	 */
+	private Double r0_Version;
+	/**
+	 * 商户编号
+	 */
+	private String r1_MerchantNo;
+	/**
+	 * 商户订单号
+	 */
+	private String r2_OrderNo;
+	/**
+	 * 商户退款订单号
+	 */
+	private String r3_RefundOrderNo;
+	/**
+	 * 退款金额
+	 */
+	private Double r4_RefundAmount;
+	/**
+	 * 商户退款流水号
+	 */
+	private String r5_RefundTrxNo;
+	/**
+	 * 退款完成时间
+	 */
+	private String r6_RefundCompleteTime;
+	/**
+	 * 退款渠道
+	 */
+	private String r7_RefundWay;
+	/**
+	 * 退款入账账户
+	 */
+	private String r8_ReceiveAccountNo;
+	/**
+	 * 退款状态 100:成功;101:失败
+	 */
+	private String ra_Status;
+	/**
+	 * 响应码
+	 */
+	private String rb_Code;
+	/**
+	 * 响应码描述
+	 */
+	private String rc_CodeMsg;
+	/**
+	 * 签名数据
+	 */
+	private String hmac;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/RefundResult.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/RefundResult.java
new file mode 100644
index 0000000..ca01e6f
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/RefundResult.java
@@ -0,0 +1,55 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+
+@Data
+public class RefundResult {
+	/**
+	 * 版本号
+	 */
+	private Double r0_Version;
+	/**
+	 * 商户编号
+	 */
+	private String r1_MerchantNo;
+	/**
+	 * 商户订单号
+	 */
+	private String r2_OrderNo;
+	/**
+	 * 商户退款订单号
+	 */
+	private String r3_RefundOrderNo;
+	/**
+	 * 退款金额
+	 */
+	private Double r4_RefundAmount;
+	/**
+	 * 商户退款流水号
+	 */
+	private String r5_RefundTrxNo;
+	/**
+	 * 退款申请状态
+	 * 100:成功,
+	 * 101:失败 。
+	 */
+	private String ra_Status;
+	/**
+	 * 响应码
+	 */
+	private String rb_Code;
+	/**
+	 * 响应码描述
+	 */
+	private String rc_CodeMsg;
+	/**
+	 * 营销退款金额
+	 */
+	private Double rd_MarketRefAmount;
+	/**
+	 * 签名数据
+	 */
+	private String hmac;
+	
+	
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/SMSUtil.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/SMSUtil.java
new file mode 100644
index 0000000..3d8df5c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/SMSUtil.java
@@ -0,0 +1,176 @@
+package com.ruoyi.errand.utils;
+
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.util.EntityUtils;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.*;
+/**
+ * @author zhibing.pu
+ * @Date 2024/12/19 9:11
+ */
+public class SMSUtil {
+	//无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
+	private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
+	//无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
+	private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
+	
+	
+	
+	/**
+	 * 调用短信
+	 * @param code  入参
+	 * @param phone 接收短信手机号
+	 * @param sender 国内短信签名通道号或国际/港澳台短信通道号
+	 * @param templateId  模板ID
+	 * @throws Exception
+	 */
+	public static void sendSms(String code,String phone,String sender,String templateId) {
+		try {
+			//必填,请参考"开发准备"获取如下数据,替换为实际值
+			String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址+接口访问URI
+			String appKey = ""; //todo APP_Key
+			String appSecret = ""; //APP_Secret
+			
+			//条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
+			//国际/港澳台短信不用关注该参数
+			String signature = "社区代办"; //签名名称
+			
+			//必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
+			String receiver = "+86" + phone; //短信接收人号码
+			
+			//选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
+			String statusCallBack = "";
+			
+			/**
+			 * 选填,使用无变量模板时请赋空值 String templateParas = "";
+			 * 单变量模板示例:模板内容为"您的验证码是${NUM_6}"时,templateParas可填写为"[\"369751\"]"
+			 * 双变量模板示例:模板内容为"您有${NUM_2}件快递请到${TXT_20}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"
+			 * ${DATE}${TIME}变量不允许取值为空,${TXT_20}变量可以使用英文空格或点号替代空值,${NUM_6}变量可以使用0替代空值
+			 * 查看更多模板和变量规范:产品介绍>模板和变量规范
+			 */
+			String templateParas = code; //模板变量
+			
+			//请求Body,不携带签名名称时,signature请填null
+			String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);
+			if (null == body || body.isEmpty()) {
+				System.out.println("body is null.");
+				return;
+			}
+			
+			//请求Headers中的X-WSSE参数值
+			String wsseHeader = buildWsseHeader(appKey, appSecret);
+			if (null == wsseHeader || wsseHeader.isEmpty()) {
+				System.out.println("wsse header is null.");
+				return;
+			}
+			
+			//如果JDK版本低于1.8,可使用如下代码
+			//为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
+			//CloseableHttpClient client = HttpClients.custom()
+			//        .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
+			//            @Override
+			//            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+			//                return true;
+			//            }
+			//        }).build()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();
+			
+			//如果JDK版本是1.8,可使用如下代码
+			//为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
+			CloseableHttpClient client = HttpClients.custom()
+					.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null,
+							(x509CertChain, authType) -> true).build())
+					.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
+					.build();
+			
+			HttpResponse response = client.execute(RequestBuilder.create("POST")//请求方法POST
+					.setUri(url)
+					.addHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
+					.addHeader(HttpHeaders.AUTHORIZATION, AUTH_HEADER_VALUE)
+					.addHeader("X-WSSE", wsseHeader)
+					.setEntity(new StringEntity(body)).build());
+			
+			System.out.println(response.toString()); //打印响应头域信息
+			System.out.println(EntityUtils.toString(response.getEntity())); //打印响应消息实体
+		}catch (Exception e){
+			e.printStackTrace();
+		}
+	}
+	
+	/**
+	 * 构造请求Body体
+	 * @param sender
+	 * @param receiver
+	 * @param templateId
+	 * @param templateParas
+	 * @param statusCallbackUrl
+	 * @param signature | 签名名称,使用国内短信通用模板时填写
+	 * @return
+	 */
+	static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
+	                               String statusCallbackUrl, String signature) {
+		if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
+				|| templateId.isEmpty()) {
+			System.out.println("buildRequestBody(): sender, receiver or templateId is null.");
+			return null;
+		}
+		List<NameValuePair> keyValues = new ArrayList<NameValuePair>();
+		
+		keyValues.add(new BasicNameValuePair("from", sender));
+		keyValues.add(new BasicNameValuePair("to", receiver));
+		keyValues.add(new BasicNameValuePair("templateId", templateId));
+		if (null != templateParas && !templateParas.isEmpty()) {
+			keyValues.add(new BasicNameValuePair("templateParas", templateParas));
+		}
+		if (null != statusCallbackUrl && !statusCallbackUrl.isEmpty()) {
+			keyValues.add(new BasicNameValuePair("statusCallback", statusCallbackUrl));
+		}
+		if (null != signature && !signature.isEmpty()) {
+			keyValues.add(new BasicNameValuePair("signature", signature));
+		}
+		
+		return URLEncodedUtils.format(keyValues, Charset.forName("UTF-8"));
+	}
+	
+	/**
+	 * 构造X-WSSE参数值
+	 * @param appKey
+	 * @param appSecret
+	 * @return
+	 */
+	static String buildWsseHeader(String appKey, String appSecret) {
+		if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
+			System.out.println("buildWsseHeader(): appKey or appSecret is null.");
+			return null;
+		}
+		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+		String time = sdf.format(new Date()); //Created
+		String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce
+		
+		byte[] passwordDigest = DigestUtils.sha256(nonce + time + appSecret);
+		String hexDigest = Hex.encodeHexString(passwordDigest);
+		
+		//如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
+		String passwordDigestBase64Str = Base64.getEncoder().encodeToString(hexDigest.getBytes()); //PasswordDigest
+		//如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
+		//String passwordDigestBase64Str = Base64.encodeBase64String(hexDigest.getBytes(Charset.forName("utf-8"))); //PasswordDigest
+		//若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
+		//passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
+		
+		return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
+	}
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/TaskUtil.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/TaskUtil.java
new file mode 100644
index 0000000..e50d245
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/TaskUtil.java
@@ -0,0 +1,34 @@
+package com.ruoyi.errand.utils;
+
+import com.ruoyi.errand.service.OrderService;
+import com.ruoyi.errand.service.VipOrderService;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+@Component
+public class TaskUtil {
+
+
+
+    
+    @Resource
+    private OrderService orderService;
+
+    @Resource
+    private VipOrderService vipOrderService;
+    
+    //每分钟
+    @Scheduled(fixedRate = 60000)
+    public void taskMonth() {
+        orderService.closeOrder();//定时任务关闭订单
+    }
+    //每分钟
+    @Scheduled(fixedRate = 60000)
+    public void task() {
+        vipOrderService.closeOrder();//定时任务关闭订单
+    }
+
+
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/TokenBlacklistService.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/TokenBlacklistService.java
new file mode 100644
index 0000000..33fda55
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/TokenBlacklistService.java
@@ -0,0 +1,93 @@
+package com.ruoyi.errand.utils;
+
+
+
+import io.jsonwebtoken.Claims;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+
+import java.util.concurrent.TimeUnit;
+
+
+@Service
+@Slf4j
+public class TokenBlacklistService {
+    private final RedisTemplate redisTemplate;
+
+    // 使用构造器注入替代字段注入
+    @Autowired
+    public TokenBlacklistService(RedisTemplate redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    // 黑名单键前缀
+    private static final String BLACKLIST_KEY_PREFIX = "token:blacklist:";
+
+    // 过期时间缓冲(防止刚好过期时还在黑名单中)
+    private static final long EXPIRATION_BUFFER_MS = 30000; // 30秒
+
+    /**
+     * 添加Token到黑名单
+     * @param token JWT Token
+     */
+    public void addToBlacklist(String token) {
+        try {
+            String realToken=token.substring(4);
+            Claims claims = JwtUtil.parseJWT(realToken);
+            Date expiration = claims.getExpiration();
+            long ttl = expiration.getTime() - System.currentTimeMillis() + EXPIRATION_BUFFER_MS;
+
+            if (ttl > 0) {
+                redisTemplate.opsForValue().set(
+                        BLACKLIST_KEY_PREFIX + realToken,
+                        "1",
+                        ttl,
+                        TimeUnit.MILLISECONDS
+                );
+                log.info("Token已加入黑名单,剩余有效期: {} 毫秒", ttl);
+            }
+        } catch (Exception e) {
+            log.error("添加Token到黑名单失败", e);
+        }
+    }
+
+    /**
+     * 检查Token是否在黑名单中
+     * @param token JWT Token
+     * @return true-在黑名单中,false-不在黑名单中
+     */
+    public boolean isBlacklisted(String token) {
+        Boolean exists = redisTemplate.hasKey(BLACKLIST_KEY_PREFIX + token);
+        return exists != null && exists;
+    }
+
+    /**
+     * 清理过期Token(Redis会自动过期,此方法可选保留)
+     */
+    @Scheduled(fixedRate = 2, timeUnit = TimeUnit.HOURS)
+    public void cleanupExpiredTokens() {
+        log.info("Token黑名单清理任务开始执行");
+        // Redis有自动过期机制,此方法主要做日志记录
+        log.info("Token黑名单清理任务完成(Redis自动处理过期)");
+    }
+
+    /**
+     * 检查JWT Token是否过期
+     * @param token JWT Token字符串
+     * @return true-已过期,false-未过期
+     */
+    public boolean isTokenExpired(String token) {
+        try {
+            Claims claims = JwtUtil.parseJWT(token);
+            return claims.getExpiration().before(new Date());
+        } catch (Exception e) {
+            // 如果解析过程中出现异常(如Token无效、已过期等),视为已过期
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayCallbackResult.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayCallbackResult.java
new file mode 100644
index 0000000..eca484c
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayCallbackResult.java
@@ -0,0 +1,89 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+
+@Data
+public class UniPayCallbackResult {
+	/**
+	 * 版本号
+	 */
+	private Double r0_Version;
+	/**
+	 * 商户编号
+	 */
+	private String r1_MerchantNo;
+	/**
+	 * 商户订单号
+	 */
+	private String r2_OrderNo;
+	/**
+	 * 支付金额
+	 */
+	private Double r3_Amount;
+	/**
+	 * 交易币种
+	 */
+	private String r4_Cur;
+	/**
+	 * 公用回传参数
+	 */
+	private String r5_Mp;
+	/**
+	 * 支付状态
+	 * 100:支付成功;
+	 * 101:支付失败。
+	 */
+	private String r6_Status;
+	/**
+	 * 交易流水号
+	 */
+	private String r7_TrxNo;
+	/**
+	 * 银行订单号
+	 */
+	private String r8_BankOrderNo;
+	/**
+	 * 银行流水号
+	 */
+	private String r9_BankTrxNo;
+	/**
+	 * 支付时间
+	 */
+	private String ra_PayTime;
+	/**
+	 * 交易结果通知时间
+	 */
+	private String rb_DealTime;
+	/**
+	 * 银行编码
+	 */
+	private String rc_BankCode;
+	/**
+	 * 用户标识
+	 */
+	private String rd_OpenId;
+	/**
+	 * 平台优惠金额
+	 */
+	private Double re_DiscountAmount;
+	/**
+	 * 卡类型
+	 */
+	private String rh_cardType;
+	/**
+	 * 订单手续费
+	 */
+	private Double rj_Fee;
+	/**
+	 * 交易类型
+	 */
+	private String rk_FrpCode;
+	/**
+	 * 签约 ID
+	 */
+	private String rl_ContractId;
+	/**
+	 * 签名数据
+	 */
+	private String hmac;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayResult.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayResult.java
new file mode 100644
index 0000000..e8e3c66
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/UniPayResult.java
@@ -0,0 +1,73 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+
+@Data
+public class UniPayResult {
+	/**
+	 * 版本号
+	 */
+	private Double r0_Version;
+	/**
+	 * 商户编号
+	 */
+	private String r1_MerchantNo;
+	/**
+	 * 商户订单号
+	 */
+	private String r2_OrderNo;
+	/**
+	 * 支付金额
+	 */
+	private Double r3_Amount;
+	/**
+	 * 币种
+	 */
+	private String r4_Cur;
+	/**
+	 * 公用回传参数
+	 */
+	private String r5_Mp;
+	/**
+	 * 交易类型
+	 */
+	private String r6_FrpCode;
+	/**
+	 * 交易流水号
+	 */
+	private String r7_TrxNo;
+	/**
+	 * 银行商户编码
+	 */
+	private String r8_MerchantBankCode;
+	/**
+	 * 响应码,返回 100 时表示成功
+	 */
+	private String ra_Code;
+	/**
+	 * 响应码描述
+	 */
+	private String rb_CodeMsg;
+	/**
+	 * 1.主扫支付返回二维码地址。
+	 * 2.支付宝 H5,mode1/2/3 参考请求参数q9_TransactionModel 说明。
+	 * 3.微信 H5_PLUS,获取支付信息的 openlink,通过手机端浏览器跳转并唤起微信 APP客户端,直接打开对应的小程序进行支付。
+	 * 3.公众号支付:需要商户参考微信的官方文档 JSAPI 支付接口进行处理,详情请见:https://pay.weixin.qq.com/wiki/doc/api/index.html
+	 * 4.微信小程序支付返回支付信息。
+	 * 5.支付宝收银台返回支付宝收银台跳转链接,通过请求该链接跳转至支付宝。
+	 * 6.微信 app3 支付,返回预支付信息,集成微信 SDK 唤起小程序进行支付。
+	 * 7.支付宝服务窗支付返回银联交易号 trade_no,可用以唤起支付宝 APP,调起支付宝APP 收银台。
+	 * 8.银联 app 或银联统一收银台支付,返回预支付信息用此网址的接口调起支付。https://open.unionpay.com/tjweb/acproduct/list?apiservId=450#nav02
+	 * 9.银联云微小程序返回跳转地址,格式:{“cqpMpAppId”:”云闪付小程序 id”,”cqpMpPath”:”云闪付小程序 path”}
+	 * 10.其他类型支付返回支付信息。
+	 */
+	private String rc_Result;
+	/**
+	 * 二维码图片码
+	 */
+	private String rd_Pic;
+	/**
+	 * 签名数据
+	 */
+	private String hmac;
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/WXCore.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/WXCore.java
new file mode 100644
index 0000000..ceb0113
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/WXCore.java
@@ -0,0 +1,48 @@
+package com.ruoyi.errand.utils;
+
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.beans.factory.annotation.Value;
+
+public class WXCore {
+
+    private static final String WATERMARK = "watermark";
+
+    @Value("${wx.appletsAppid}")
+    private static String appid ;
+
+
+
+    /**
+     * 解密数据
+     * @return
+     * @throws Exception
+     */
+    public static String decrypt(String encryptedData, String sessionKey, String iv){
+        String result = "";
+        try {
+            AES aes = new AES();
+            byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
+            if(null != resultByte && resultByte.length > 0){
+                result = new String(WxPKCS7Encoder.decode(resultByte), "UTF-8");
+//                JSONObject jsonObject = JSON.parseObject(result);
+//                String decryptAppid = jsonObject.getJSONObject(WATERMARK).getString("appid");
+//                if(!appid.equals(decryptAppid)){
+//                    result = "";
+//                }
+            }
+        } catch (Exception e) {
+            result = "";
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+
+    public static void main(String[] args) throws Exception{
+        String appId = "wx4f4bc4dec97d474b";
+        String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==";
+        String sessionKey = "tiihtNczf5v6AKRyjwEUhQ==";
+        String iv = "r7BXXKkLb8qrSNn05n0qiA==";
+        System.out.println(decrypt(encryptedData, sessionKey, iv));
+    }
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppAuthenticationToken.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppAuthenticationToken.java
new file mode 100644
index 0000000..3959d6d
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppAuthenticationToken.java
@@ -0,0 +1,29 @@
+package com.ruoyi.errand.utils;
+
+import com.ruoyi.errand.domain.AppUser;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+import java.util.ArrayList;
+
+public class WeAppAuthenticationToken extends AbstractAuthenticationToken {
+
+    private final AppUser appuser; // 微信OpenID作为主体标识
+    private final String token;  // 原始Token(可选)
+
+    public WeAppAuthenticationToken(String openid, AppUser appuser, String token) {
+        super(new ArrayList<>()); // 无权限集合
+        this.appuser = appuser;
+        this.token = token;
+        setAuthenticated(true); // 标记已认证
+    }
+
+    @Override
+    public String getCredentials() {
+        return this.token; // 返回Token(或null)
+    }
+
+    @Override
+    public AppUser getPrincipal() {
+        return this.appuser; // 返回OpenID作为身份主体
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppSecurityProperties.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppSecurityProperties.java
new file mode 100644
index 0000000..cbe67a2
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/WeAppSecurityProperties.java
@@ -0,0 +1,15 @@
+package com.ruoyi.errand.utils;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+@ConfigurationProperties(prefix = "weapp.security")
+@Data
+public class WeAppSecurityProperties {
+    private List<String> excludeUrls = new ArrayList<>();
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/WeChatUtil.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/WeChatUtil.java
new file mode 100644
index 0000000..8cf2513
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/WeChatUtil.java
@@ -0,0 +1,244 @@
+package com.ruoyi.errand.utils;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.http.Header;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
+
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 微信工具类
+ */
+@Slf4j
+@Component
+public class WeChatUtil {
+
+    @Value("${wx.appletsAppid}")
+    private String wxAppletsAppid;
+
+    @Value("${wx.appletsAppSecret}")
+    private String wxAppletsAppSecret;
+
+//    @Value("${wx.appid}")
+    private String webAppId;
+
+//    @Value("${wx.appSecret}")
+    private String webAppSecret;
+    
+    @Resource
+    private RedisService redisService;
+    
+    
+
+
+    /**
+     * 小程序使用jscode获取openid
+     * @param jscode
+     * @return
+     */
+    public Map<String, Object> code2Session(String jscode) {
+        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret
+                + "&js_code=" + jscode + "&grant_type=authorization_code";
+        HttpRequest get = HttpUtil.createGet(url);
+        HttpResponse response = get.execute();
+        int status = response.getStatus();
+        if(200 != status){
+            throw new RuntimeException(response.body());
+        }
+        JSONObject jsonObject = JSONObject.parseObject(response.body());
+        int errcode = jsonObject.getIntValue("errcode");
+        Map<String, Object> map = new HashMap<>();
+        map.put("errcode", errcode);
+        if(errcode == 0){//成功
+            map.put("openid", jsonObject.getString("openid"));
+            map.put("sessionKey", jsonObject.getString("session_key"));
+            map.put("unionid", jsonObject.getString("unionid"));
+            return map;
+        }
+        if(errcode == -1){//系统繁忙,此时请开发者稍候再试
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 40029){//code 无效
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 45011){//频率限制,每个用户每分钟100次
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        return null;
+    }
+    
+    
+    public String getWxAppletsAccessToken(){
+        Object wxAppletsAccessToken = redisService.getCacheObject("wxAppletsAccessToken");
+        if(null != wxAppletsAccessToken){
+            return wxAppletsAccessToken.toString();
+        }
+        String appletsAccessToken = getAppletsAccessToken();
+        redisService.setCacheObject("wxAppletsAccessToken", appletsAccessToken, 7200L, TimeUnit.SECONDS);
+        return appletsAccessToken;
+    }
+    
+
+    /**
+     * 获取微信小程序token
+     * @return
+     */
+    public String getAppletsAccessToken() {
+        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret;
+        HttpRequest get = HttpUtil.createGet(url);
+        HttpResponse response = get.execute();
+        if(response.getStatus() != 200){
+            return "";
+        }
+        JSONObject jsonObject = JSONObject.parseObject(response.body());
+        return jsonObject.getString("access_token");
+    }
+
+
+    /**
+     * 网站应用登录
+     * @param code
+     * @return
+     */
+    public Map<String, String> webAccessToken(String code) throws Exception{
+        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + webAppId + "&secret=" + webAppSecret + "&code=" + code + "&grant_type=authorization_code";
+        HttpRequest get = HttpUtil.createGet(url);
+        HttpResponse response = get.execute();
+        if(response.getStatus() != 200){
+            return null;
+        }
+        JSONObject jsonObject = JSONObject.parseObject(response.body());
+        int errcode = jsonObject.getIntValue("errcode");
+        Map<String, String> map = new HashMap<>();
+        if(errcode == 0){//成功
+            map.put("access_token", jsonObject.getString("access_token"));
+            map.put("openid", jsonObject.getString("openid"));
+            map.put("refresh_token", jsonObject.getString("refresh_token"));
+            map.put("unionid", jsonObject.getString("unionid"));
+            return map;
+        }
+        if(errcode == -1){//系统繁忙,此时请开发者稍候再试
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 40029){//code 无效
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 45011){//频率限制,每个用户每分钟100次
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        return map;
+    }
+
+
+    /**
+     * 获取微信个人信息
+     * @param access_token
+     * @param openid
+     * @return
+     */
+    public Map<String, Object> getUserInfo(String access_token, String openid) throws Exception{
+        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid;
+        HttpRequest get = HttpUtil.createGet(url);
+        HttpResponse response = get.execute();
+        if(response.getStatus() != 200){
+            return null;
+        }
+        JSONObject jsonObject = JSONObject.parseObject(response.body());
+        int errcode = jsonObject.getIntValue("errcode");
+        Map<String, Object> map = new HashMap<>();
+        if(errcode == 0){//成功
+            map.put("nickname", jsonObject.getString("nickname"));
+            map.put("openid", jsonObject.getString("openid"));
+            map.put("sex", jsonObject.getString("sex"));
+            map.put("headimgurl", jsonObject.getString("headimgurl"));
+            return map;
+        }
+        if(errcode == -1){//系统繁忙,此时请开发者稍候再试
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 40029){//code 无效
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        if(errcode == 45011){//频率限制,每个用户每分钟100次
+            map.put("msg", jsonObject.getString("errmsg"));
+            return map;
+        }
+        return map;
+    }
+
+    
+
+
+    /**
+     * 获取小程序二维码
+     * @param page      跳转页 例如 pages/index/index
+     * @param scene     参数 a=1&b=2
+     */
+/*    public InputStream getwxacodeunlimit(String page, String scene, EnvVersion env_version){
+        try {
+            String token = getWxAppletsAccessToken();
+            if(StringUtils.isEmpty(token)){
+                System.err.println("获取接口调用凭证失败");
+            }
+            String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + token;
+            Map<String, Object> param = new HashMap<>();
+            param.put("scene", scene);
+            param.put("page", page);
+            param.put("env_version", env_version.getVersion());
+    
+            HttpRequest post = HttpUtil.createPost(url);
+            post.header(Header.CONTENT_TYPE, "application/json;charset=UTF-8");
+            post.body(JSONObject.toJSONString(param));
+            HttpResponse execute = post.execute();
+            byte[] bytes = execute.bodyBytes();
+            String body1 = execute.body();
+            return new ByteArrayInputStream(bytes);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+    */
+    
+    /**
+     * 获取微信小程序二维码
+     * @param page
+     * @param scene
+     * @param filePath
+     * @return
+     */
+  /*  public String getwxacodeunlimit(String page, String scene, EnvVersion env_version, String filePath){
+        InputStream getwxacodeunlimit = getwxacodeunlimit(page, scene, env_version);
+//        File file = FileUtil.writeFromStream(getwxacodeunlimit, new File(filePath));
+//        return file.getPath();
+        return ObsUploadUtil.obsUpload("png", getwxacodeunlimit);
+    }*/
+}
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/WebSocketConfig.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/WebSocketConfig.java
new file mode 100644
index 0000000..683bdc3
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/WebSocketConfig.java
@@ -0,0 +1,28 @@
+package com.ruoyi.errand.utils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
+
+@Configuration
+@EnableWebSocket
+public class WebSocketConfig {
+
+    @Bean
+    public ServletServerContainerFactoryBean createWebSocketContainer() {
+        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
+        container.setMaxTextMessageBufferSize(8192);
+        container.setMaxBinaryMessageBufferSize(8192);
+        return container;
+    }
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter(); // 自动注册@ServerEndpoint注解的类
+    }
+}
\ No newline at end of file
diff --git a/pt-errand/src/main/java/com/ruoyi/errand/utils/WxPKCS7Encoder.java b/pt-errand/src/main/java/com/ruoyi/errand/utils/WxPKCS7Encoder.java
new file mode 100644
index 0000000..14542b7
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/utils/WxPKCS7Encoder.java
@@ -0,0 +1,63 @@
+package com.ruoyi.errand.utils;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+* 微信小程序加解密
+* @author pzb
+* @Date 2021/12/3 15:43
+*/
+public class WxPKCS7Encoder {
+    private static final Charset CHARSET = Charset.forName("utf-8");
+    private static final int BLOCK_SIZE = 32;
+
+    /**
+     * 获得对明文进行补位填充的字节.
+     *
+     * @param count
+     *            需要进行填充补位操作的明文字节个数
+     * @return 补齐用的字节数组
+     */
+    public static byte[] encode(int count) {
+        // 计算需要填充的位数
+        int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
+        if (amountToPad == 0) {
+            amountToPad = BLOCK_SIZE;
+        }
+        // 获得补位所用的字符
+        char padChr = chr(amountToPad);
+        String tmp = new String();
+        for (int index = 0; index < amountToPad; index++) {
+            tmp += padChr;
+        }
+        return tmp.getBytes(CHARSET);
+    }
+
+    /**
+     * 删除解密后明文的补位字符
+     *
+     * @param decrypted
+     *            解密后的明文
+     * @return 删除补位字符后的明文
+     */
+    public static byte[] decode(byte[] decrypted) {
+        int pad = decrypted[decrypted.length - 1];
+        if (pad < 1 || pad > 32) {
+            pad = 0;
+        }
+        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
+    }
+
+    /**
+     * 将数字转化成ASCII码对应的字符,用于对明文进行补码
+     *
+     * @param a
+     *            需要转化的数字
+     * @return 转化得到的字符
+     */
+    public static char chr(int a) {
+        byte target = (byte) (a & 0xFF);
+        return (char) target;
+    }
+}
diff --git a/pt-errand/src/main/resources/mapper/AddressBookMapper.xml b/pt-errand/src/main/resources/mapper/AddressBookMapper.xml
new file mode 100644
index 0000000..9b99b14
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/AddressBookMapper.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.AddressBookMapper">
+
+
+
+    <select id="addressBookByCommunityId" resultType="com.ruoyi.errand.object.vo.app.AddressBookByCommunityIdVO">
+        select
+            id,
+            recipient_name,
+            recipient_phone,
+            address_detail,
+            is_default
+        from
+            t_address_book
+        where
+            del_flag = 0
+        and community_id =#{communityId}
+        and app_user_id =#{userId}
+        ORDER BY tab.is_default DESC , tab.create_time DESC
+    </select>
+
+    <select id="addressBookList" resultType="com.ruoyi.errand.object.vo.app.AddressBookListVO">
+        select
+            tab.id,
+            tab.community_id,
+            tc.communityName,
+            tab.recipient_name,
+            tab.recipient_phone,
+            tab.address_detail,
+            tab.is_default
+        from
+            t_address_book  tab
+        inner join
+            t_community  tc on tab.community_id =tc.id
+        where
+            tab.del_flag = 0
+          and tc.del_flag=0
+          and tab.app_user_id =#{userId}
+        ORDER BY tab.community_id ASC, tab.is_default DESC , tab.create_time DESC
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/AgreementMapper.xml b/pt-errand/src/main/resources/mapper/AgreementMapper.xml
new file mode 100644
index 0000000..e214f50
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/AgreementMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.AgreementMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/AppUserMapper.xml b/pt-errand/src/main/resources/mapper/AppUserMapper.xml
new file mode 100644
index 0000000..a963aca
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/AppUserMapper.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.AppUserMapper">
+
+
+    <select id="getOrderPage" resultType="com.ruoyi.errand.object.vo.app.OrderPageVO">
+        SELECT
+            tc.id AS communityId,
+            tc.name AS communityName,
+            tc.fee_amount AS feeAmount,
+            tab.id AS addressBookId,
+            tab.recipient_name AS recipientName,
+            tab.recipient_phone AS recipientPhone,
+            tab.address_detail AS addressDetail,
+            tau.phone as phone,
+            tau.vip_id AS vipId,
+            tau.first_order as isFirstOrder,
+            tau.end_time as endTime
+        FROM
+            t_app_user tau
+                LEFT JOIN
+            t_community tc ON
+                tc.id=tau.community_id
+                    AND tc.del_flag = 0
+                    AND tc.status = 0
+                LEFT JOIN
+            t_address_book tab
+            ON tau.id = tab.app_user_id
+                and tc.id = tab.community_id
+                AND tc.del_flag = 0
+                AND tc.status = 0
+        WHERE
+            tau.id = #{userId}
+        ORDER BY tab.is_default DESC , tab.create_time DESC
+
+
+
+    </select>
+    <select id="getMyInfo" resultType="com.ruoyi.errand.object.vo.app.AppUserInfoVO">
+        select
+            tau.avatar,
+            tau.name,
+            tau.phone,
+            tau.sex,
+            tau.vip_id,
+            tvs.vip_name as vipName,
+            tau.endTime
+        from
+            t_app_user tau
+        left join
+            t_vip_setting tvs on tau.vip_id = tvs.id
+        where
+            tau.del_flag=0
+       and tau.id = #{id}
+    </select>
+
+    <select id="countByCreateTimeBefore" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM t_app_user WHERE create_time &lt;= #{end} and del_flag=0 and status !=3
+    </select>
+
+    <select id="countByCreateTimeBetween" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM t_app_user WHERE create_time BETWEEN #{start} AND #{end}  and del_flag=0 and status !=3
+    </select>
+
+    <select id="countGroupByDate" resultType="java.util.Map">
+        SELECT
+            CASE #{datePattern}
+                WHEN 'HH时' THEN DATE_FORMAT(create_time, '%H时')
+                WHEN 'EEEE' THEN ELT(DAYOFWEEK(create_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+                WHEN 'dd日' THEN DATE_FORMAT(create_time, '%d日')
+                WHEN 'MM月' THEN DATE_FORMAT(create_time, '%m月')
+                WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(create_time, '%Y-%m-%d')
+                ELSE DATE_FORMAT(create_time, '%Y-%m-%d')
+                END AS `date`,
+            COUNT(*) AS `count`
+        FROM t_app_user
+        WHERE create_time BETWEEN #{start} AND #{end}
+          and del_flag=0 and status !=3
+        GROUP BY
+            CASE #{datePattern}
+            WHEN 'HH时' THEN DATE_FORMAT(create_time, '%H时')
+            WHEN 'EEEE' THEN ELT(DAYOFWEEK(create_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+            WHEN 'dd日' THEN DATE_FORMAT(create_time, '%d日')
+            WHEN 'MM月' THEN DATE_FORMAT(create_time, '%m月')
+            WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(create_time, '%Y-%m-%d')
+            ELSE DATE_FORMAT(create_time, '%Y-%m-%d')
+        END
+        ORDER BY create_time
+    </select>
+    <select id="getCourierByCommunityId" resultType="java.util.Map">
+        select
+            tau.id as `id`,
+            tau.wx_openid as `openid`,
+            tc.phone as `phone`
+        from
+            t_community_courier tcc
+        left join t_app_user tau on tcc.courier_id = tau.courier_id
+        left join t_courier tc on tcc.courier_id = tc.id
+        where
+            tcc.community_id=#{communityId}
+
+    </select>
+    <select id="getAppUserPageList" resultType="com.ruoyi.errand.object.vo.sys.AppUserPageListVO">
+        SELECT
+            id,
+            name,
+            phone,
+            CASE WHEN end_time IS NOT NULL AND end_time > CURRENT_TIMESTAMP THEN 1 ELSE 0 END AS isVip,
+            create_time,
+            status
+        FROM
+            t_app_user
+        where
+            del_flag=0
+        <if test="dto.name!=null and '' != dto.name  ">
+            and name like concat('%',#{dto.name},'%')
+        </if>
+        <if test="dto.phone!=null and '' != dto.phone">
+            and phone like concat('%',#{dto.phone},'%')
+        </if>
+        <if test="dto.isVip!=null and dto.isVip == 1">
+            and end_time > CURRENT_TIMESTAMP
+        </if>
+        <if test="dto.isVip!=null and dto.isVip == 0">
+            and end_time &lt;  CURRENT_TIMESTAMP or end_time is null
+        </if>
+        <if test="dto.status!=null  ">
+            and status =#{status}
+        </if>
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/BannerMapper.xml b/pt-errand/src/main/resources/mapper/BannerMapper.xml
new file mode 100644
index 0000000..834f4aa
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/BannerMapper.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.BannerMapper">
+
+
+    <select id="getBannerList" resultType="com.ruoyi.errand.object.vo.app.BannerVO">
+        select id ,name,image_url from t_banner where del_flag = 0
+    </select>
+    <select id="pageList" resultType="com.ruoyi.errand.object.vo.sys.BannerPageListVO">
+        select id, name from t_banner where del_flag=0
+        <if test="name!=null and name!='' ">
+            and name like concat('%',#{name},'%')
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/CommunityCourierMapper.xml b/pt-errand/src/main/resources/mapper/CommunityCourierMapper.xml
new file mode 100644
index 0000000..7333ce3
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/CommunityCourierMapper.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.CommunityCourierMapper">
+
+
+    <select id="getAllCourierList" resultType="java.lang.Integer">
+        select
+            courier_id
+        from
+            t_community_courier
+    </select>
+    <select id="getAllCommunityList" resultType="java.lang.Integer">
+        select
+            community_id
+        from
+            t_community_courier
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/CommunityMapper.xml b/pt-errand/src/main/resources/mapper/CommunityMapper.xml
new file mode 100644
index 0000000..4b9dfda
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/CommunityMapper.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.CommunityMapper">
+
+    <resultMap id="communityListVO" type="com.ruoyi.errand.object.vo.app.CommunityListVO">
+        <id column="id" property="id"/>
+        <result column="name" property="name"/>
+    </resultMap>
+
+    <select id="selectListByRegionId"  resultMap="communityListVO">
+        select id ,name
+        from t_community where region_id = #{regionId}
+        and del_flag=0
+        and status = 0
+    </select>
+    <select id="getTotalCommunityList" resultType="com.ruoyi.errand.object.vo.app.CommunityListVO">
+        select id ,name
+        from t_community where del_flag=0
+    </select>
+    <select id="getAllCommunityList" resultType="com.ruoyi.errand.object.vo.sys.AllCommunityListVO">
+        select
+            id as communityId,
+            name as name
+        from
+        t_community
+        where
+            del_flag=0
+        and
+        id not in
+        <foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+    <select id="getCommunityPageList" resultType="com.ruoyi.errand.object.vo.sys.CommunityPageListVO">
+        select
+            tc.id,
+            tc.name as name,
+            tr.name as regionName,
+            tc.admin_name as adminName,
+            tc.admin_phone as adminPhone,
+            tco.name as courierName,
+            tc.status as status
+            from    t_community tc
+        left join  t_region tr on tc.region_id = tr.id
+        left join t_community_courier tcc on tc.id = tcc.community_id
+        left join t_courier tco on tcc.courier_id = toc.id
+        where
+            del_flag=0
+        <if test="dto.name !=null  and ''!= dto.name">
+            and tc.name like concat('%',#{dto.name},'%')
+        </if>
+        <if test="dto.regionId!=null">
+            and tc.region_id =#{dto.regionId}
+        </if>
+        <if test="dto.adminName !=null  and ''!= dto.adminName">
+            and tc.admin_name like concat('%',#{dto.adminName},'%')
+        </if>
+        <if test="dto.adminPhone !=null  and ''!= dto.adminPhone">
+            and tc.admin_phone like concat('%',#{dto.adminPhone},'%')
+        </if>
+        <if test="dto.courierName !=null  and ''!= dto.courierName">
+            and tco.name like concat('%',#{dto.courierName},'%')
+        </if>
+        <if test="dto.status!=null">
+            and tc.status =#{dto.status}
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/CourierMapper.xml b/pt-errand/src/main/resources/mapper/CourierMapper.xml
new file mode 100644
index 0000000..64f9e41
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/CourierMapper.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.CourierMapper">
+
+
+    <select id="getCourierInfo" resultType="com.ruoyi.errand.object.vo.app.CourierInfoVO">
+        select
+            tc.name as name,
+            tc.phone as phone,
+            tc2.name as communityName
+        from t_courier tc
+        left join t_community_courier on tc.id = tcc.courier_id
+        left join t_community tc2 on tcc.community_id = tc2.id
+        where
+            tc.del_flag=0
+            and tc.id=#{courierId}
+    </select>
+    <select id="getDatStatistics" resultType="com.ruoyi.errand.object.vo.app.CourierStatisticsVO">
+        SELECT
+            COUNT(CASE WHEN DATE(receiving_time) = CURDATE() THEN 1 END) AS `day`,
+            COUNT(CASE WHEN YEARWEEK(receiving_time, 1) = YEARWEEK(CURDATE(), 1) THEN 1 END) AS `week`,
+            COUNT(CASE WHEN YEAR(receiving_time) = YEAR(CURDATE())
+                AND MONTH(receiving_time) = MONTH(CURDATE()) THEN 1 END) AS `month`
+        FROM t_order
+        WHERE courier_id = #{courierId}
+          AND order_status IN (2, 4, 5) -- 进行中、已完成、已评价状态
+          AND receiving_time IS NOT NULL;
+    </select>
+    <select id="getCourierOrderList" resultType="com.ruoyi.errand.object.vo.app.CourierOrderListVO">
+        select
+            o.id as id,
+            o.address_detail as addressDetail,
+            o.recipient_name as recipientName,
+            o.recipient_phone as recipientPhone,
+            o.order_status as orderStatus,
+            o.receiving_time as receivingTime,
+            te.rating as rating
+        from
+            t_order o
+        left join  t_evaluation te on o.id = te.order_id
+            o.courier_id = #{courierId}  -- 替换为实际跑腿ID
+            AND o.del_flag = 0          -- 过滤未删除的订单
+        and o.payStatus = 2            -- 过滤未支付的订单
+        <if test="orderStatus !=null and orderStatus !=0">
+            AND o.order_status = #{orderStatus}  -- 订单状态筛选条件
+        </if>
+        ORDER BY
+            CASE WHEN o.order_status = 1 THEN 0 ELSE 1 END,  -- 待确认订单置顶
+    o.order_time DESC;  -- 其余订单按下单时间倒序
+    </select>
+    <select id="getCourierPageList" resultType="com.ruoyi.errand.object.vo.sys.CourierPageListVO">
+        select
+        tc.id as id,
+        tc.name as name,
+        tc.phone as phone,
+        tcm.name as communityName,
+        tc.status as status
+        from t_courier tc
+        left join t_community_courier tcc on tc.id = tcc.courier_id
+        left join t_community tcm on tcc.community_id = tcm.id
+        where
+            tc.del_flag=0
+        <if test="dto.name !=null and ''!=dto.name">
+            AND tc.name = #{dto.name}
+        </if>
+        <if test="dto.phone !=null and ''!=dto.phone">
+            AND tc.phone = #{dto.phone}
+        </if>
+        <if test="dto.status !=null ">
+            AND tc.status = #{dto.status}
+        </if>
+    </select>
+    <select id="detail" resultType="com.ruoyi.errand.object.vo.sys.CourierSysDetailVO">
+        select
+            tc.name as name,
+            tc.phone as phone,
+            tc.id_card as idCard,
+            tcc.community_id as communityId
+            tcm.name as communityName,
+            tc.create_time as createTime
+            tc.status as status
+        from t_courier tc
+                 left join t_community_courier tcc on tc.id = tcc.courier_id
+                 left join t_community tcm on tcc.community_id = tcm.id
+        where
+            tc.id=#{id}
+    </select>
+    <select id="getAllCourierList" resultType="com.ruoyi.errand.object.vo.sys.AllCourierListVO">
+        select
+            id as courierId,
+            name as name
+        from
+            t_courier
+        where
+            del_flag=0
+        and
+            id not in
+        <foreach collection="couriers" item="item" index="index" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/EvaluationMapper.xml b/pt-errand/src/main/resources/mapper/EvaluationMapper.xml
new file mode 100644
index 0000000..aeb23cb
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/EvaluationMapper.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.EvaluationMapper">
+
+
+    <select id="getFiveCount" resultType="java.lang.Integer">
+        select
+            count(*)
+        from t_evaluation te
+        inner join t_order o on te.order_id =o.id
+        where
+            o.courier_id=#{courierId}
+        and o.del_flag=0
+        and te.rating=5.0
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/FeedbackMapper.xml b/pt-errand/src/main/resources/mapper/FeedbackMapper.xml
new file mode 100644
index 0000000..95d4c7b
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/FeedbackMapper.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.FeedbackMapper">
+
+
+    <select id="getFeedbackPageList" resultType="com.ruoyi.errand.object.vo.sys.FeedbackPageListVO">
+        select
+            id,name,phone,content,create_time,status
+        from
+            t_feedback
+        where
+            del_flag=0
+        <if test="dto.name != null and dto.name !='' ">
+            and name like concat('%',#{dto.name},'%')
+        </if>
+        <if test="dto.phone != null and dto.phone !='' ">
+            and phone like concat('%',#{dto.phone},'%')
+        </if>
+        <if test="dto.status != null  ">
+            and status= #{dto.status}
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/OrderMapper.xml b/pt-errand/src/main/resources/mapper/OrderMapper.xml
new file mode 100644
index 0000000..1bca70e
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/OrderMapper.xml
@@ -0,0 +1,426 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.OrderMapper">
+
+
+    <select id="getAppUserOrderList" resultType="com.ruoyi.errand.object.vo.app.AppUserOrderListVO">
+        select
+            o.id,
+            o.address_detail,
+            o.recipient_name,
+            o.recipient_phone,
+            o.order_status,
+            o.order_time,
+            o.rating,
+            o.courier_id,
+            c.name,
+            c.phone
+            from t_order o
+            left join t_courier c on o.courier_id = c.id
+            where
+                o.del_flag=0
+              and o.pay_status=2
+              and o.app_user_id = #{appUserId}
+              <if test="orderStatus!=null and orderStatus!=0">
+                  and o.order_status = #{orderStatus}
+              </if>
+    </select>
+    <select id="getOrderDetail" resultType="com.ruoyi.errand.object.vo.app.OrderDetailVO">
+        select
+            o.id as id,
+            o.community_id as communityId,
+            o.community_name as communityName,
+            o.address_detail as addressDetail,
+            o.recipient_name as recipientName,
+            o.recipient_phone as recipientPhone,
+            o.agency_matters as agencyMatters,
+            o.num as num,
+            o.remark as remark,
+            o.pics as pics,
+            o.courier_pics as courierPics,
+            o.order_status as orderStatus,
+            o.order_number as orderNumber,
+            o.order_time as orderTime,
+            o.pay_method as payMethod,
+            o.payment_amount as paymentAmount,
+            c.courier_name as courierName,
+            c.courier_phone as courierPhone,
+            e.rating as rating,
+            e.content as content
+            from t_order o
+        left join t_courier c on o.courier_id = c.id
+        left join t_evaluation e on o.id = e.order_id
+        where
+            o.id=#{id}
+            and o.del_flag=0
+            and e.type = 0
+    </select>
+
+    <select id="getOrderTopInfoByDate" resultType="java.util.Map">
+        select
+            count(*) as num,
+        COALESCE(SUM(payment_amount), 0.00) AS amount
+        from
+            t_order
+        where
+            del_flag=0
+        and
+            order_status in (4,5)
+        <if test="communityId!=null and communityId!=0">
+            and communityId =#{communityId}
+        </if>
+        <if test="start!=null and end!=null">
+            and order_time BETWEEN #{start} AND #{end}
+        </if>
+
+
+    </select>
+    <select id="countGroupByDate" resultType="java.util.Map">
+        SELECT
+            CASE #{datePattern}
+                WHEN 'HH时' THEN DATE_FORMAT(order_time, '%H时')
+                WHEN 'EEEE' THEN ELT(DAYOFWEEK(order_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+                WHEN 'dd日' THEN DATE_FORMAT(order_time, '%d日')
+                WHEN 'MM月' THEN DATE_FORMAT(order_time, '%m月')
+                WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(order_time, '%Y-%m-%d')
+                ELSE DATE_FORMAT(order_time, '%Y-%m-%d')
+                END AS `date`,
+            COUNT(*) AS `num`,
+            COALESCE(SUM(payment_amount), 0.00) AS amount
+        FROM t_order
+        WHERE order_time BETWEEN #{start} AND #{end}
+          and del_flag=0 and order_status in (4,5)
+        GROUP BY
+            CASE #{datePattern}
+                WHEN 'HH时' THEN DATE_FORMAT(order_time, '%H时')
+                WHEN 'EEEE' THEN ELT(DAYOFWEEK(order_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+                WHEN 'dd日' THEN DATE_FORMAT(order_time, '%d日')
+                WHEN 'MM月' THEN DATE_FORMAT(order_time, '%m月')
+                WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(order_time, '%Y-%m-%d')
+                ELSE DATE_FORMAT(order_time, '%Y-%m-%d')
+                END
+        ORDER BY order_time
+    </select>
+
+    <select id="selectPageTotal" resultType="java.lang.Long">
+        SELECT COUNT(*) FROM (
+            SELECT tau.id, 1 as type, t.order_status, t.order_time
+            FROM t_order t
+            INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+            WHERE
+            t.del_flag=0
+              and t.pay_status=2
+            <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                and tau.name like concat('%',#{dto.appUserName},'%')
+            </if>
+
+            <if test="dto.phone!=null and  ''!=dto.phone">
+                and tau.phone like concat('%',#{dto.phone},'%')
+            </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+            <if test="dto.startTime!=null and dto.endTime != null">
+                and t.order_time between #{dto.startTime} and #{dto.endTime}
+            </if>
+        UNION ALL
+            SELECT tau.id, 2 as type, t.order_status, t.order_time
+            FROM t_vip_order t
+            INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+            WHERE t.del_flag=0
+        and t.pay_status=2
+            <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                and tau.name like concat('%',#{dto.appUserName},'%')
+            </if>
+
+            <if test="dto.phone!=null and  ''!=dto.phone">
+                and tau.phone like concat('%',#{dto.phone},'%')
+            </if>
+
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+            <if test="dto.startTime!=null and dto.endTime != null">
+                and t.order_time between #{dto.startTime} and #{dto.endTime}
+            </if>
+        ) t
+        where
+            1=1
+        <if test="dto.type!=null and 0!=dto.type">
+            and t.type = #{dto.type}
+        </if>
+    </select>
+
+
+    <select id="financeStatistics" resultType="com.ruoyi.errand.object.vo.sys.FinanceStatisticsVO">
+        select * from (
+                SELECT t.id as id,
+                       t.order_time as orderTime,
+                       1 as type,
+                       t.order_number as orderNumber,
+                       tau.name as appUserName,
+                       tau.phone as phone,
+                       t.order_status as orderStatus
+                FROM t_order t
+                INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+                WHERE t.del_flag=0
+        and t.pay_status=2
+                <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                    and tau.name like concat('%',#{dto.appUserName},'%')
+                </if>
+                <if test="dto.phone!=null and  ''!=dto.phone">
+                    and tau.phone like concat('%',#{dto.phone},'%')
+                </if>
+                <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+                    and t.order_status = #{dto.orderStatus}
+                </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+                <if test="dto.startTime!=null and dto.endTime != null">
+                    and t.order_time between #{dto.startTime} and #{dto.endTime}
+                </if>
+            UNION ALL
+                SELECT t.id as id,
+                t.order_time as orderTime,
+                2 as type,
+                t.order_number as orderNumber,
+                tau.name as appUserName,
+                tau.phone as phone,
+                t.order_status as orderStatus
+                FROM t_vip_order t
+                INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+                WHERE t.del_flag=0
+        and t.pay_status=2
+                <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                    and tau.name like concat('%',#{dto.appUserName},'%')
+                </if>
+
+                <if test="dto.phone!=null and  ''!=dto.phone">
+                    and tau.phone like concat('%',#{dto.phone},'%')
+                </if>
+
+                <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+                    and t.order_status = #{dto.orderStatus}
+                </if>
+                <if test="dto.orderStatus ==4">
+                    and t.order_status in(4,5)
+                </if>
+
+                <if test="dto.startTime!=null and dto.endTime != null">
+                    and t.order_time between #{dto.startTime} and #{dto.endTime}
+                </if>
+                ) t
+            where
+            1=1
+            <if test="dto.type!=null and 0!=dto.type">
+                and t.type = #{dto.type}
+            </if>
+            ORDER BY t.orderTime DESC
+            LIMIT #{dto.pageNum}, #{dto.pageSize}
+    </select>
+    <select id="getOrderPageList" resultType="com.ruoyi.errand.object.vo.sys.OrderPageListVO">
+        select
+            o.id as id,
+            o.order_number as orderNumber,
+            o.order_time as orderTime,
+            o.payment_amount as paymentAmount,
+            o.finish_time as finishTime,
+            o.order_status as orderStatus,
+            o.community_name as communityName,
+            tau.name as appUserName,
+            tau.phone as appUserPhone,
+            tc.name as courierName,
+            tc.phone as courierPhone
+        from t_order o
+        inner join t_app_user tau on o.app_user_id = tau.id
+        left join t_courier tc on o.courier_id = tc.id
+        where
+            o.del_flag=0
+        and o.pay_status=2
+        <if test="dto.orderNumber!=null and ''!=dto.orderNumber">
+            and o.order_number like concat('%',#{dto.orderNumber},'%')
+        </if>
+        <if test="dto.communityName!=null and ''!=dto.communityName">
+            and o.community_name like concat('%',#{dto.communityName},'%')
+        </if>
+        <if test="dto.appUserName!=null and ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.appUserPhone!=null and ''!=dto.appUserPhone">
+            and tau.phone like concat('%',#{dto.appUserPhone},'%')
+        </if>
+        <if test="dto.courierName!=null and ''!=dto.courierName">
+            and tc.name like concat('%',#{dto.courierName},'%')
+        </if>
+        <if test="dto.courierPhone!=null and ''!=dto.courierPhone">
+            and tc.phone like concat('%',#{dto.courierPhone},'%')
+        </if>
+        <if test="dto.orderStartTime!=null and dto.orderEndTime != null">
+            and o.order_time between #{dto.orderStartTime} and #{dto.orderEndTime}
+        </if>
+        <if test="dto.finishStartTime!=null and dto.finishEndTime != null">
+            and o.finishTime between #{dto.finishStartTime} and #{dto.finishEndTime}
+        </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and o.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and o.order_status in(4,5)
+        </if>
+    </select>
+    <select id="detail" resultType="com.ruoyi.errand.object.vo.sys.OrderSysDetailVO">
+        select
+            o.order_status as orderStatus,
+            tau.name as appUserName,
+            tau.phone as appUserPhone,
+            o.community_name as communityName,
+            o.recipient_name as recipientName,
+            o.recipient_phone as recipientPhone,
+            o.address_detail as addressDetail,
+            o.agency_matters as agencyMatters,
+            o.num as  num,
+            o.remark as  remark,
+            o.pics as  pics,
+            tc.name as  courierName,
+            tc.phone as  courierPhone,
+            o.courier_pics as  courierPics,
+            o.order_number as  orderNumber,
+            o.order_time as  orderTime,
+            o.finish_time as  finishTime,
+            o.payment_amount as  paymentAmount,
+            teu.rating as  userRating,
+            teu.content as  userContent,
+            tec.rating as  courierRating,
+            tec.content as courierContent
+        from t_order o
+        inner join t_app_user tau on o.app_user_id = tau.id
+        left join t_courier tc on o.courier_id = tc.id
+        left join t_evaluation teu on o.id = teu.order_id and teu.type=0
+        left join t_evaluation tec on o.id = teu.order_id and teu.type=1
+        where
+            o.id=#{id}
+    </select>
+    <select id="export" resultType="com.ruoyi.errand.object.vo.sys.FinanceStatisticsVO">
+        select * from (
+        SELECT t.id as id,
+        t.order_time as orderTime,
+        1 as type,
+        t.order_number as orderNumber,
+        tau.name as appUserName,
+        tau.phone as phone,
+        t.order_status as orderStatus
+        FROM t_order t
+        INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+        WHERE t.del_flag=0
+        and t.pay_status=2
+        <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.phone!=null and  ''!=dto.phone">
+            and tau.phone like concat('%',#{dto.phone},'%')
+        </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+        <if test="dto.startTime!=null and dto.endTime != null">
+            and t.order_time between #{dto.startTime} and #{dto.endTime}
+        </if>
+        UNION ALL
+        SELECT t.id as id,
+        t.order_time as orderTime,
+        2 as type,
+        t.order_number as orderNumber,
+        tau.name as appUserName,
+        tau.phone as phone,
+        t.order_status as orderStatus
+        FROM t_vip_order t
+        INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+        WHERE t.del_flag=0
+        and t.pay_status=2
+        <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+
+        <if test="dto.phone!=null and  ''!=dto.phone">
+            and tau.phone like concat('%',#{dto.phone},'%')
+        </if>
+
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+
+        <if test="dto.startTime!=null and dto.endTime != null">
+            and t.order_time between #{dto.startTime} and #{dto.endTime}
+        </if>
+        ) t
+        where
+        1=1
+        <if test="dto.type!=null and 0!=dto.type">
+            and t.type = #{dto.type}
+        </if>
+        ORDER BY t.orderTime DESC
+    </select>
+    <select id="orderExport" resultType="com.ruoyi.errand.object.vo.sys.OrderPageListVO">
+        select
+        o.id as id,
+        o.order_number as orderNumber,
+        o.order_time as orderTime,
+        o.payment_amount as paymentAmount,
+        o.finish_time as finishTime,
+        o.order_status as orderStatus,
+        o.community_name as communityName,
+        tau.name as appUserName,
+        tau.phone as appUserPhone,
+        tc.name as courierName,
+        tc.phone as courierPhone
+        from t_order o
+        inner join t_app_user tau on o.app_user_id = tau.id
+        left join t_courier tc on o.courier_id = tc.id
+        where
+        o.del_flag=0
+        and o.pay_status=2
+        <if test="dto.orderNumber!=null and ''!=dto.orderNumber">
+            and o.order_number like concat('%',#{dto.orderNumber},'%')
+        </if>
+        <if test="dto.communityName!=null and ''!=dto.communityName">
+            and o.community_name like concat('%',#{dto.communityName},'%')
+        </if>
+        <if test="dto.appUserName!=null and ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.appUserPhone!=null and ''!=dto.appUserPhone">
+            and tau.phone like concat('%',#{dto.appUserPhone},'%')
+        </if>
+        <if test="dto.courierName!=null and ''!=dto.courierName">
+            and tc.name like concat('%',#{dto.courierName},'%')
+        </if>
+        <if test="dto.courierPhone!=null and ''!=dto.courierPhone">
+            and tc.phone like concat('%',#{dto.courierPhone},'%')
+        </if>
+        <if test="dto.orderStartTime!=null and dto.orderEndTime != null">
+            and o.order_time between #{dto.orderStartTime} and #{dto.orderEndTime}
+        </if>
+        <if test="dto.finishStartTime!=null and dto.finishEndTime != null">
+            and o.finishTime between #{dto.finishStartTime} and #{dto.finishEndTime}
+        </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and o.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and o.order_status in(4,5)
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/PhoneMapper.xml b/pt-errand/src/main/resources/mapper/PhoneMapper.xml
new file mode 100644
index 0000000..87e0a9f
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/PhoneMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.PhoneMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/RegionMapper.xml b/pt-errand/src/main/resources/mapper/RegionMapper.xml
new file mode 100644
index 0000000..8dd1eba
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/RegionMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.RegionMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/ReportMapper.xml b/pt-errand/src/main/resources/mapper/ReportMapper.xml
new file mode 100644
index 0000000..731ee39
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/ReportMapper.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.ReportMapper">
+
+
+    <select id="getReportList" resultType="com.ruoyi.errand.object.vo.sys.ReportPageListVO">
+        select
+            tr.id as id,
+            tr.community_name as communityName,
+            tre.name as regionName,
+            tr.address_detail as addressDetail,
+            tau.name as appUserName,
+            tau.phone as appUserPhone,
+            tr.create_time as createTime,
+            tr.status as status
+            from
+                t_report tr
+        left join t_app_user tau on tr.app_user_id = tau.id
+        left join t_region tre on tr.region_id = tre.id
+        where
+            tr.del_flag=0
+        <if test="dto.communityName!=null and ''!=dto.communityName">
+            and tr.community_name like concat('%',#{dto.communityName},'%')
+        </if>
+        <if test="dto.regionId!=null">
+            and tre.id =#{dto.regionId}
+        </if>
+        <if test="dto.appUserName!=null and ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.appUserPhone!=null and ''!=dto.appUserPhone">
+            and tau.phone like concat('%',#{dto.appUserPhone},'%')
+        </if>
+        <if test="dto.status!=null">
+            and tr.status =#{dto.status}
+        </if>
+
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/SystemConfigMapper.xml b/pt-errand/src/main/resources/mapper/SystemConfigMapper.xml
new file mode 100644
index 0000000..5c1b2e6
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/SystemConfigMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.SystemConfigMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/UserCancellationLogMapper.xml b/pt-errand/src/main/resources/mapper/UserCancellationLogMapper.xml
new file mode 100644
index 0000000..b92b748
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/UserCancellationLogMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.UserCancellationLogMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/VipOrderMapper.xml b/pt-errand/src/main/resources/mapper/VipOrderMapper.xml
new file mode 100644
index 0000000..90d44c9
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/VipOrderMapper.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.VipOrderMapper">
+
+
+    <select id="selectRefundVipOrder" resultType="com.ruoyi.errand.domain.VipOrder">
+        select order_number,payment_amount
+        from t_vip_order tvo
+        left join
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/mapper/VipSettingMapper.xml b/pt-errand/src/main/resources/mapper/VipSettingMapper.xml
new file mode 100644
index 0000000..6e6523e
--- /dev/null
+++ b/pt-errand/src/main/resources/mapper/VipSettingMapper.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.VipSettingMapper">
+
+
+    <select id="getVipInfoList" resultType="com.ruoyi.errand.object.vo.app.VipInfoListVO">
+        select id,vip_name,vip_price
+        from
+            t_vip_setting
+        order by id asc
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/src/main/resources/templates/bjd.xlsx b/pt-errand/src/main/resources/templates/bjd.xlsx
new file mode 100644
index 0000000..94d32d1
--- /dev/null
+++ b/pt-errand/src/main/resources/templates/bjd.xlsx
Binary files differ
diff --git a/pt-errand/target/classes/mapper/AddressBookMapper.xml b/pt-errand/target/classes/mapper/AddressBookMapper.xml
new file mode 100644
index 0000000..9b99b14
--- /dev/null
+++ b/pt-errand/target/classes/mapper/AddressBookMapper.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.AddressBookMapper">
+
+
+
+    <select id="addressBookByCommunityId" resultType="com.ruoyi.errand.object.vo.app.AddressBookByCommunityIdVO">
+        select
+            id,
+            recipient_name,
+            recipient_phone,
+            address_detail,
+            is_default
+        from
+            t_address_book
+        where
+            del_flag = 0
+        and community_id =#{communityId}
+        and app_user_id =#{userId}
+        ORDER BY tab.is_default DESC , tab.create_time DESC
+    </select>
+
+    <select id="addressBookList" resultType="com.ruoyi.errand.object.vo.app.AddressBookListVO">
+        select
+            tab.id,
+            tab.community_id,
+            tc.communityName,
+            tab.recipient_name,
+            tab.recipient_phone,
+            tab.address_detail,
+            tab.is_default
+        from
+            t_address_book  tab
+        inner join
+            t_community  tc on tab.community_id =tc.id
+        where
+            tab.del_flag = 0
+          and tc.del_flag=0
+          and tab.app_user_id =#{userId}
+        ORDER BY tab.community_id ASC, tab.is_default DESC , tab.create_time DESC
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/AgreementMapper.xml b/pt-errand/target/classes/mapper/AgreementMapper.xml
new file mode 100644
index 0000000..e214f50
--- /dev/null
+++ b/pt-errand/target/classes/mapper/AgreementMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.AgreementMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/AppUserMapper.xml b/pt-errand/target/classes/mapper/AppUserMapper.xml
new file mode 100644
index 0000000..a963aca
--- /dev/null
+++ b/pt-errand/target/classes/mapper/AppUserMapper.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.AppUserMapper">
+
+
+    <select id="getOrderPage" resultType="com.ruoyi.errand.object.vo.app.OrderPageVO">
+        SELECT
+            tc.id AS communityId,
+            tc.name AS communityName,
+            tc.fee_amount AS feeAmount,
+            tab.id AS addressBookId,
+            tab.recipient_name AS recipientName,
+            tab.recipient_phone AS recipientPhone,
+            tab.address_detail AS addressDetail,
+            tau.phone as phone,
+            tau.vip_id AS vipId,
+            tau.first_order as isFirstOrder,
+            tau.end_time as endTime
+        FROM
+            t_app_user tau
+                LEFT JOIN
+            t_community tc ON
+                tc.id=tau.community_id
+                    AND tc.del_flag = 0
+                    AND tc.status = 0
+                LEFT JOIN
+            t_address_book tab
+            ON tau.id = tab.app_user_id
+                and tc.id = tab.community_id
+                AND tc.del_flag = 0
+                AND tc.status = 0
+        WHERE
+            tau.id = #{userId}
+        ORDER BY tab.is_default DESC , tab.create_time DESC
+
+
+
+    </select>
+    <select id="getMyInfo" resultType="com.ruoyi.errand.object.vo.app.AppUserInfoVO">
+        select
+            tau.avatar,
+            tau.name,
+            tau.phone,
+            tau.sex,
+            tau.vip_id,
+            tvs.vip_name as vipName,
+            tau.endTime
+        from
+            t_app_user tau
+        left join
+            t_vip_setting tvs on tau.vip_id = tvs.id
+        where
+            tau.del_flag=0
+       and tau.id = #{id}
+    </select>
+
+    <select id="countByCreateTimeBefore" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM t_app_user WHERE create_time &lt;= #{end} and del_flag=0 and status !=3
+    </select>
+
+    <select id="countByCreateTimeBetween" resultType="java.lang.Integer">
+        SELECT COUNT(*) FROM t_app_user WHERE create_time BETWEEN #{start} AND #{end}  and del_flag=0 and status !=3
+    </select>
+
+    <select id="countGroupByDate" resultType="java.util.Map">
+        SELECT
+            CASE #{datePattern}
+                WHEN 'HH时' THEN DATE_FORMAT(create_time, '%H时')
+                WHEN 'EEEE' THEN ELT(DAYOFWEEK(create_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+                WHEN 'dd日' THEN DATE_FORMAT(create_time, '%d日')
+                WHEN 'MM月' THEN DATE_FORMAT(create_time, '%m月')
+                WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(create_time, '%Y-%m-%d')
+                ELSE DATE_FORMAT(create_time, '%Y-%m-%d')
+                END AS `date`,
+            COUNT(*) AS `count`
+        FROM t_app_user
+        WHERE create_time BETWEEN #{start} AND #{end}
+          and del_flag=0 and status !=3
+        GROUP BY
+            CASE #{datePattern}
+            WHEN 'HH时' THEN DATE_FORMAT(create_time, '%H时')
+            WHEN 'EEEE' THEN ELT(DAYOFWEEK(create_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+            WHEN 'dd日' THEN DATE_FORMAT(create_time, '%d日')
+            WHEN 'MM月' THEN DATE_FORMAT(create_time, '%m月')
+            WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(create_time, '%Y-%m-%d')
+            ELSE DATE_FORMAT(create_time, '%Y-%m-%d')
+        END
+        ORDER BY create_time
+    </select>
+    <select id="getCourierByCommunityId" resultType="java.util.Map">
+        select
+            tau.id as `id`,
+            tau.wx_openid as `openid`,
+            tc.phone as `phone`
+        from
+            t_community_courier tcc
+        left join t_app_user tau on tcc.courier_id = tau.courier_id
+        left join t_courier tc on tcc.courier_id = tc.id
+        where
+            tcc.community_id=#{communityId}
+
+    </select>
+    <select id="getAppUserPageList" resultType="com.ruoyi.errand.object.vo.sys.AppUserPageListVO">
+        SELECT
+            id,
+            name,
+            phone,
+            CASE WHEN end_time IS NOT NULL AND end_time > CURRENT_TIMESTAMP THEN 1 ELSE 0 END AS isVip,
+            create_time,
+            status
+        FROM
+            t_app_user
+        where
+            del_flag=0
+        <if test="dto.name!=null and '' != dto.name  ">
+            and name like concat('%',#{dto.name},'%')
+        </if>
+        <if test="dto.phone!=null and '' != dto.phone">
+            and phone like concat('%',#{dto.phone},'%')
+        </if>
+        <if test="dto.isVip!=null and dto.isVip == 1">
+            and end_time > CURRENT_TIMESTAMP
+        </if>
+        <if test="dto.isVip!=null and dto.isVip == 0">
+            and end_time &lt;  CURRENT_TIMESTAMP or end_time is null
+        </if>
+        <if test="dto.status!=null  ">
+            and status =#{status}
+        </if>
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/BannerMapper.xml b/pt-errand/target/classes/mapper/BannerMapper.xml
new file mode 100644
index 0000000..834f4aa
--- /dev/null
+++ b/pt-errand/target/classes/mapper/BannerMapper.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.BannerMapper">
+
+
+    <select id="getBannerList" resultType="com.ruoyi.errand.object.vo.app.BannerVO">
+        select id ,name,image_url from t_banner where del_flag = 0
+    </select>
+    <select id="pageList" resultType="com.ruoyi.errand.object.vo.sys.BannerPageListVO">
+        select id, name from t_banner where del_flag=0
+        <if test="name!=null and name!='' ">
+            and name like concat('%',#{name},'%')
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/CommunityCourierMapper.xml b/pt-errand/target/classes/mapper/CommunityCourierMapper.xml
new file mode 100644
index 0000000..7333ce3
--- /dev/null
+++ b/pt-errand/target/classes/mapper/CommunityCourierMapper.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.CommunityCourierMapper">
+
+
+    <select id="getAllCourierList" resultType="java.lang.Integer">
+        select
+            courier_id
+        from
+            t_community_courier
+    </select>
+    <select id="getAllCommunityList" resultType="java.lang.Integer">
+        select
+            community_id
+        from
+            t_community_courier
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/CommunityMapper.xml b/pt-errand/target/classes/mapper/CommunityMapper.xml
new file mode 100644
index 0000000..4b9dfda
--- /dev/null
+++ b/pt-errand/target/classes/mapper/CommunityMapper.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.CommunityMapper">
+
+    <resultMap id="communityListVO" type="com.ruoyi.errand.object.vo.app.CommunityListVO">
+        <id column="id" property="id"/>
+        <result column="name" property="name"/>
+    </resultMap>
+
+    <select id="selectListByRegionId"  resultMap="communityListVO">
+        select id ,name
+        from t_community where region_id = #{regionId}
+        and del_flag=0
+        and status = 0
+    </select>
+    <select id="getTotalCommunityList" resultType="com.ruoyi.errand.object.vo.app.CommunityListVO">
+        select id ,name
+        from t_community where del_flag=0
+    </select>
+    <select id="getAllCommunityList" resultType="com.ruoyi.errand.object.vo.sys.AllCommunityListVO">
+        select
+            id as communityId,
+            name as name
+        from
+        t_community
+        where
+            del_flag=0
+        and
+        id not in
+        <foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+    <select id="getCommunityPageList" resultType="com.ruoyi.errand.object.vo.sys.CommunityPageListVO">
+        select
+            tc.id,
+            tc.name as name,
+            tr.name as regionName,
+            tc.admin_name as adminName,
+            tc.admin_phone as adminPhone,
+            tco.name as courierName,
+            tc.status as status
+            from    t_community tc
+        left join  t_region tr on tc.region_id = tr.id
+        left join t_community_courier tcc on tc.id = tcc.community_id
+        left join t_courier tco on tcc.courier_id = toc.id
+        where
+            del_flag=0
+        <if test="dto.name !=null  and ''!= dto.name">
+            and tc.name like concat('%',#{dto.name},'%')
+        </if>
+        <if test="dto.regionId!=null">
+            and tc.region_id =#{dto.regionId}
+        </if>
+        <if test="dto.adminName !=null  and ''!= dto.adminName">
+            and tc.admin_name like concat('%',#{dto.adminName},'%')
+        </if>
+        <if test="dto.adminPhone !=null  and ''!= dto.adminPhone">
+            and tc.admin_phone like concat('%',#{dto.adminPhone},'%')
+        </if>
+        <if test="dto.courierName !=null  and ''!= dto.courierName">
+            and tco.name like concat('%',#{dto.courierName},'%')
+        </if>
+        <if test="dto.status!=null">
+            and tc.status =#{dto.status}
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/CourierMapper.xml b/pt-errand/target/classes/mapper/CourierMapper.xml
new file mode 100644
index 0000000..64f9e41
--- /dev/null
+++ b/pt-errand/target/classes/mapper/CourierMapper.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.CourierMapper">
+
+
+    <select id="getCourierInfo" resultType="com.ruoyi.errand.object.vo.app.CourierInfoVO">
+        select
+            tc.name as name,
+            tc.phone as phone,
+            tc2.name as communityName
+        from t_courier tc
+        left join t_community_courier on tc.id = tcc.courier_id
+        left join t_community tc2 on tcc.community_id = tc2.id
+        where
+            tc.del_flag=0
+            and tc.id=#{courierId}
+    </select>
+    <select id="getDatStatistics" resultType="com.ruoyi.errand.object.vo.app.CourierStatisticsVO">
+        SELECT
+            COUNT(CASE WHEN DATE(receiving_time) = CURDATE() THEN 1 END) AS `day`,
+            COUNT(CASE WHEN YEARWEEK(receiving_time, 1) = YEARWEEK(CURDATE(), 1) THEN 1 END) AS `week`,
+            COUNT(CASE WHEN YEAR(receiving_time) = YEAR(CURDATE())
+                AND MONTH(receiving_time) = MONTH(CURDATE()) THEN 1 END) AS `month`
+        FROM t_order
+        WHERE courier_id = #{courierId}
+          AND order_status IN (2, 4, 5) -- 进行中、已完成、已评价状态
+          AND receiving_time IS NOT NULL;
+    </select>
+    <select id="getCourierOrderList" resultType="com.ruoyi.errand.object.vo.app.CourierOrderListVO">
+        select
+            o.id as id,
+            o.address_detail as addressDetail,
+            o.recipient_name as recipientName,
+            o.recipient_phone as recipientPhone,
+            o.order_status as orderStatus,
+            o.receiving_time as receivingTime,
+            te.rating as rating
+        from
+            t_order o
+        left join  t_evaluation te on o.id = te.order_id
+            o.courier_id = #{courierId}  -- 替换为实际跑腿ID
+            AND o.del_flag = 0          -- 过滤未删除的订单
+        and o.payStatus = 2            -- 过滤未支付的订单
+        <if test="orderStatus !=null and orderStatus !=0">
+            AND o.order_status = #{orderStatus}  -- 订单状态筛选条件
+        </if>
+        ORDER BY
+            CASE WHEN o.order_status = 1 THEN 0 ELSE 1 END,  -- 待确认订单置顶
+    o.order_time DESC;  -- 其余订单按下单时间倒序
+    </select>
+    <select id="getCourierPageList" resultType="com.ruoyi.errand.object.vo.sys.CourierPageListVO">
+        select
+        tc.id as id,
+        tc.name as name,
+        tc.phone as phone,
+        tcm.name as communityName,
+        tc.status as status
+        from t_courier tc
+        left join t_community_courier tcc on tc.id = tcc.courier_id
+        left join t_community tcm on tcc.community_id = tcm.id
+        where
+            tc.del_flag=0
+        <if test="dto.name !=null and ''!=dto.name">
+            AND tc.name = #{dto.name}
+        </if>
+        <if test="dto.phone !=null and ''!=dto.phone">
+            AND tc.phone = #{dto.phone}
+        </if>
+        <if test="dto.status !=null ">
+            AND tc.status = #{dto.status}
+        </if>
+    </select>
+    <select id="detail" resultType="com.ruoyi.errand.object.vo.sys.CourierSysDetailVO">
+        select
+            tc.name as name,
+            tc.phone as phone,
+            tc.id_card as idCard,
+            tcc.community_id as communityId
+            tcm.name as communityName,
+            tc.create_time as createTime
+            tc.status as status
+        from t_courier tc
+                 left join t_community_courier tcc on tc.id = tcc.courier_id
+                 left join t_community tcm on tcc.community_id = tcm.id
+        where
+            tc.id=#{id}
+    </select>
+    <select id="getAllCourierList" resultType="com.ruoyi.errand.object.vo.sys.AllCourierListVO">
+        select
+            id as courierId,
+            name as name
+        from
+            t_courier
+        where
+            del_flag=0
+        and
+            id not in
+        <foreach collection="couriers" item="item" index="index" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/EvaluationMapper.xml b/pt-errand/target/classes/mapper/EvaluationMapper.xml
new file mode 100644
index 0000000..aeb23cb
--- /dev/null
+++ b/pt-errand/target/classes/mapper/EvaluationMapper.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.EvaluationMapper">
+
+
+    <select id="getFiveCount" resultType="java.lang.Integer">
+        select
+            count(*)
+        from t_evaluation te
+        inner join t_order o on te.order_id =o.id
+        where
+            o.courier_id=#{courierId}
+        and o.del_flag=0
+        and te.rating=5.0
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/FeedbackMapper.xml b/pt-errand/target/classes/mapper/FeedbackMapper.xml
new file mode 100644
index 0000000..95d4c7b
--- /dev/null
+++ b/pt-errand/target/classes/mapper/FeedbackMapper.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.FeedbackMapper">
+
+
+    <select id="getFeedbackPageList" resultType="com.ruoyi.errand.object.vo.sys.FeedbackPageListVO">
+        select
+            id,name,phone,content,create_time,status
+        from
+            t_feedback
+        where
+            del_flag=0
+        <if test="dto.name != null and dto.name !='' ">
+            and name like concat('%',#{dto.name},'%')
+        </if>
+        <if test="dto.phone != null and dto.phone !='' ">
+            and phone like concat('%',#{dto.phone},'%')
+        </if>
+        <if test="dto.status != null  ">
+            and status= #{dto.status}
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/OrderMapper.xml b/pt-errand/target/classes/mapper/OrderMapper.xml
new file mode 100644
index 0000000..1bca70e
--- /dev/null
+++ b/pt-errand/target/classes/mapper/OrderMapper.xml
@@ -0,0 +1,426 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.OrderMapper">
+
+
+    <select id="getAppUserOrderList" resultType="com.ruoyi.errand.object.vo.app.AppUserOrderListVO">
+        select
+            o.id,
+            o.address_detail,
+            o.recipient_name,
+            o.recipient_phone,
+            o.order_status,
+            o.order_time,
+            o.rating,
+            o.courier_id,
+            c.name,
+            c.phone
+            from t_order o
+            left join t_courier c on o.courier_id = c.id
+            where
+                o.del_flag=0
+              and o.pay_status=2
+              and o.app_user_id = #{appUserId}
+              <if test="orderStatus!=null and orderStatus!=0">
+                  and o.order_status = #{orderStatus}
+              </if>
+    </select>
+    <select id="getOrderDetail" resultType="com.ruoyi.errand.object.vo.app.OrderDetailVO">
+        select
+            o.id as id,
+            o.community_id as communityId,
+            o.community_name as communityName,
+            o.address_detail as addressDetail,
+            o.recipient_name as recipientName,
+            o.recipient_phone as recipientPhone,
+            o.agency_matters as agencyMatters,
+            o.num as num,
+            o.remark as remark,
+            o.pics as pics,
+            o.courier_pics as courierPics,
+            o.order_status as orderStatus,
+            o.order_number as orderNumber,
+            o.order_time as orderTime,
+            o.pay_method as payMethod,
+            o.payment_amount as paymentAmount,
+            c.courier_name as courierName,
+            c.courier_phone as courierPhone,
+            e.rating as rating,
+            e.content as content
+            from t_order o
+        left join t_courier c on o.courier_id = c.id
+        left join t_evaluation e on o.id = e.order_id
+        where
+            o.id=#{id}
+            and o.del_flag=0
+            and e.type = 0
+    </select>
+
+    <select id="getOrderTopInfoByDate" resultType="java.util.Map">
+        select
+            count(*) as num,
+        COALESCE(SUM(payment_amount), 0.00) AS amount
+        from
+            t_order
+        where
+            del_flag=0
+        and
+            order_status in (4,5)
+        <if test="communityId!=null and communityId!=0">
+            and communityId =#{communityId}
+        </if>
+        <if test="start!=null and end!=null">
+            and order_time BETWEEN #{start} AND #{end}
+        </if>
+
+
+    </select>
+    <select id="countGroupByDate" resultType="java.util.Map">
+        SELECT
+            CASE #{datePattern}
+                WHEN 'HH时' THEN DATE_FORMAT(order_time, '%H时')
+                WHEN 'EEEE' THEN ELT(DAYOFWEEK(order_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+                WHEN 'dd日' THEN DATE_FORMAT(order_time, '%d日')
+                WHEN 'MM月' THEN DATE_FORMAT(order_time, '%m月')
+                WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(order_time, '%Y-%m-%d')
+                ELSE DATE_FORMAT(order_time, '%Y-%m-%d')
+                END AS `date`,
+            COUNT(*) AS `num`,
+            COALESCE(SUM(payment_amount), 0.00) AS amount
+        FROM t_order
+        WHERE order_time BETWEEN #{start} AND #{end}
+          and del_flag=0 and order_status in (4,5)
+        GROUP BY
+            CASE #{datePattern}
+                WHEN 'HH时' THEN DATE_FORMAT(order_time, '%H时')
+                WHEN 'EEEE' THEN ELT(DAYOFWEEK(order_time), '星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六')
+                WHEN 'dd日' THEN DATE_FORMAT(order_time, '%d日')
+                WHEN 'MM月' THEN DATE_FORMAT(order_time, '%m月')
+                WHEN 'yyyy-MM-dd' THEN DATE_FORMAT(order_time, '%Y-%m-%d')
+                ELSE DATE_FORMAT(order_time, '%Y-%m-%d')
+                END
+        ORDER BY order_time
+    </select>
+
+    <select id="selectPageTotal" resultType="java.lang.Long">
+        SELECT COUNT(*) FROM (
+            SELECT tau.id, 1 as type, t.order_status, t.order_time
+            FROM t_order t
+            INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+            WHERE
+            t.del_flag=0
+              and t.pay_status=2
+            <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                and tau.name like concat('%',#{dto.appUserName},'%')
+            </if>
+
+            <if test="dto.phone!=null and  ''!=dto.phone">
+                and tau.phone like concat('%',#{dto.phone},'%')
+            </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+            <if test="dto.startTime!=null and dto.endTime != null">
+                and t.order_time between #{dto.startTime} and #{dto.endTime}
+            </if>
+        UNION ALL
+            SELECT tau.id, 2 as type, t.order_status, t.order_time
+            FROM t_vip_order t
+            INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+            WHERE t.del_flag=0
+        and t.pay_status=2
+            <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                and tau.name like concat('%',#{dto.appUserName},'%')
+            </if>
+
+            <if test="dto.phone!=null and  ''!=dto.phone">
+                and tau.phone like concat('%',#{dto.phone},'%')
+            </if>
+
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+            <if test="dto.startTime!=null and dto.endTime != null">
+                and t.order_time between #{dto.startTime} and #{dto.endTime}
+            </if>
+        ) t
+        where
+            1=1
+        <if test="dto.type!=null and 0!=dto.type">
+            and t.type = #{dto.type}
+        </if>
+    </select>
+
+
+    <select id="financeStatistics" resultType="com.ruoyi.errand.object.vo.sys.FinanceStatisticsVO">
+        select * from (
+                SELECT t.id as id,
+                       t.order_time as orderTime,
+                       1 as type,
+                       t.order_number as orderNumber,
+                       tau.name as appUserName,
+                       tau.phone as phone,
+                       t.order_status as orderStatus
+                FROM t_order t
+                INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+                WHERE t.del_flag=0
+        and t.pay_status=2
+                <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                    and tau.name like concat('%',#{dto.appUserName},'%')
+                </if>
+                <if test="dto.phone!=null and  ''!=dto.phone">
+                    and tau.phone like concat('%',#{dto.phone},'%')
+                </if>
+                <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+                    and t.order_status = #{dto.orderStatus}
+                </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+                <if test="dto.startTime!=null and dto.endTime != null">
+                    and t.order_time between #{dto.startTime} and #{dto.endTime}
+                </if>
+            UNION ALL
+                SELECT t.id as id,
+                t.order_time as orderTime,
+                2 as type,
+                t.order_number as orderNumber,
+                tau.name as appUserName,
+                tau.phone as phone,
+                t.order_status as orderStatus
+                FROM t_vip_order t
+                INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+                WHERE t.del_flag=0
+        and t.pay_status=2
+                <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+                    and tau.name like concat('%',#{dto.appUserName},'%')
+                </if>
+
+                <if test="dto.phone!=null and  ''!=dto.phone">
+                    and tau.phone like concat('%',#{dto.phone},'%')
+                </if>
+
+                <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+                    and t.order_status = #{dto.orderStatus}
+                </if>
+                <if test="dto.orderStatus ==4">
+                    and t.order_status in(4,5)
+                </if>
+
+                <if test="dto.startTime!=null and dto.endTime != null">
+                    and t.order_time between #{dto.startTime} and #{dto.endTime}
+                </if>
+                ) t
+            where
+            1=1
+            <if test="dto.type!=null and 0!=dto.type">
+                and t.type = #{dto.type}
+            </if>
+            ORDER BY t.orderTime DESC
+            LIMIT #{dto.pageNum}, #{dto.pageSize}
+    </select>
+    <select id="getOrderPageList" resultType="com.ruoyi.errand.object.vo.sys.OrderPageListVO">
+        select
+            o.id as id,
+            o.order_number as orderNumber,
+            o.order_time as orderTime,
+            o.payment_amount as paymentAmount,
+            o.finish_time as finishTime,
+            o.order_status as orderStatus,
+            o.community_name as communityName,
+            tau.name as appUserName,
+            tau.phone as appUserPhone,
+            tc.name as courierName,
+            tc.phone as courierPhone
+        from t_order o
+        inner join t_app_user tau on o.app_user_id = tau.id
+        left join t_courier tc on o.courier_id = tc.id
+        where
+            o.del_flag=0
+        and o.pay_status=2
+        <if test="dto.orderNumber!=null and ''!=dto.orderNumber">
+            and o.order_number like concat('%',#{dto.orderNumber},'%')
+        </if>
+        <if test="dto.communityName!=null and ''!=dto.communityName">
+            and o.community_name like concat('%',#{dto.communityName},'%')
+        </if>
+        <if test="dto.appUserName!=null and ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.appUserPhone!=null and ''!=dto.appUserPhone">
+            and tau.phone like concat('%',#{dto.appUserPhone},'%')
+        </if>
+        <if test="dto.courierName!=null and ''!=dto.courierName">
+            and tc.name like concat('%',#{dto.courierName},'%')
+        </if>
+        <if test="dto.courierPhone!=null and ''!=dto.courierPhone">
+            and tc.phone like concat('%',#{dto.courierPhone},'%')
+        </if>
+        <if test="dto.orderStartTime!=null and dto.orderEndTime != null">
+            and o.order_time between #{dto.orderStartTime} and #{dto.orderEndTime}
+        </if>
+        <if test="dto.finishStartTime!=null and dto.finishEndTime != null">
+            and o.finishTime between #{dto.finishStartTime} and #{dto.finishEndTime}
+        </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and o.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and o.order_status in(4,5)
+        </if>
+    </select>
+    <select id="detail" resultType="com.ruoyi.errand.object.vo.sys.OrderSysDetailVO">
+        select
+            o.order_status as orderStatus,
+            tau.name as appUserName,
+            tau.phone as appUserPhone,
+            o.community_name as communityName,
+            o.recipient_name as recipientName,
+            o.recipient_phone as recipientPhone,
+            o.address_detail as addressDetail,
+            o.agency_matters as agencyMatters,
+            o.num as  num,
+            o.remark as  remark,
+            o.pics as  pics,
+            tc.name as  courierName,
+            tc.phone as  courierPhone,
+            o.courier_pics as  courierPics,
+            o.order_number as  orderNumber,
+            o.order_time as  orderTime,
+            o.finish_time as  finishTime,
+            o.payment_amount as  paymentAmount,
+            teu.rating as  userRating,
+            teu.content as  userContent,
+            tec.rating as  courierRating,
+            tec.content as courierContent
+        from t_order o
+        inner join t_app_user tau on o.app_user_id = tau.id
+        left join t_courier tc on o.courier_id = tc.id
+        left join t_evaluation teu on o.id = teu.order_id and teu.type=0
+        left join t_evaluation tec on o.id = teu.order_id and teu.type=1
+        where
+            o.id=#{id}
+    </select>
+    <select id="export" resultType="com.ruoyi.errand.object.vo.sys.FinanceStatisticsVO">
+        select * from (
+        SELECT t.id as id,
+        t.order_time as orderTime,
+        1 as type,
+        t.order_number as orderNumber,
+        tau.name as appUserName,
+        tau.phone as phone,
+        t.order_status as orderStatus
+        FROM t_order t
+        INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+        WHERE t.del_flag=0
+        and t.pay_status=2
+        <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.phone!=null and  ''!=dto.phone">
+            and tau.phone like concat('%',#{dto.phone},'%')
+        </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+        <if test="dto.startTime!=null and dto.endTime != null">
+            and t.order_time between #{dto.startTime} and #{dto.endTime}
+        </if>
+        UNION ALL
+        SELECT t.id as id,
+        t.order_time as orderTime,
+        2 as type,
+        t.order_number as orderNumber,
+        tau.name as appUserName,
+        tau.phone as phone,
+        t.order_status as orderStatus
+        FROM t_vip_order t
+        INNER JOIN t_app_user tau ON t.app_user_id = tau.id
+        WHERE t.del_flag=0
+        and t.pay_status=2
+        <if test="dto.appUserName!=null and  ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+
+        <if test="dto.phone!=null and  ''!=dto.phone">
+            and tau.phone like concat('%',#{dto.phone},'%')
+        </if>
+
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and t.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and t.order_status in(4,5)
+        </if>
+
+        <if test="dto.startTime!=null and dto.endTime != null">
+            and t.order_time between #{dto.startTime} and #{dto.endTime}
+        </if>
+        ) t
+        where
+        1=1
+        <if test="dto.type!=null and 0!=dto.type">
+            and t.type = #{dto.type}
+        </if>
+        ORDER BY t.orderTime DESC
+    </select>
+    <select id="orderExport" resultType="com.ruoyi.errand.object.vo.sys.OrderPageListVO">
+        select
+        o.id as id,
+        o.order_number as orderNumber,
+        o.order_time as orderTime,
+        o.payment_amount as paymentAmount,
+        o.finish_time as finishTime,
+        o.order_status as orderStatus,
+        o.community_name as communityName,
+        tau.name as appUserName,
+        tau.phone as appUserPhone,
+        tc.name as courierName,
+        tc.phone as courierPhone
+        from t_order o
+        inner join t_app_user tau on o.app_user_id = tau.id
+        left join t_courier tc on o.courier_id = tc.id
+        where
+        o.del_flag=0
+        and o.pay_status=2
+        <if test="dto.orderNumber!=null and ''!=dto.orderNumber">
+            and o.order_number like concat('%',#{dto.orderNumber},'%')
+        </if>
+        <if test="dto.communityName!=null and ''!=dto.communityName">
+            and o.community_name like concat('%',#{dto.communityName},'%')
+        </if>
+        <if test="dto.appUserName!=null and ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.appUserPhone!=null and ''!=dto.appUserPhone">
+            and tau.phone like concat('%',#{dto.appUserPhone},'%')
+        </if>
+        <if test="dto.courierName!=null and ''!=dto.courierName">
+            and tc.name like concat('%',#{dto.courierName},'%')
+        </if>
+        <if test="dto.courierPhone!=null and ''!=dto.courierPhone">
+            and tc.phone like concat('%',#{dto.courierPhone},'%')
+        </if>
+        <if test="dto.orderStartTime!=null and dto.orderEndTime != null">
+            and o.order_time between #{dto.orderStartTime} and #{dto.orderEndTime}
+        </if>
+        <if test="dto.finishStartTime!=null and dto.finishEndTime != null">
+            and o.finishTime between #{dto.finishStartTime} and #{dto.finishEndTime}
+        </if>
+        <if test="dto.orderStatus!=null and 0!=dto.orderStatus and dto.orderStatus !=4">
+            and o.order_status = #{dto.orderStatus}
+        </if>
+        <if test="dto.orderStatus ==4">
+            and o.order_status in(4,5)
+        </if>
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/PhoneMapper.xml b/pt-errand/target/classes/mapper/PhoneMapper.xml
new file mode 100644
index 0000000..87e0a9f
--- /dev/null
+++ b/pt-errand/target/classes/mapper/PhoneMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.PhoneMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/RegionMapper.xml b/pt-errand/target/classes/mapper/RegionMapper.xml
new file mode 100644
index 0000000..8dd1eba
--- /dev/null
+++ b/pt-errand/target/classes/mapper/RegionMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.RegionMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/ReportMapper.xml b/pt-errand/target/classes/mapper/ReportMapper.xml
new file mode 100644
index 0000000..731ee39
--- /dev/null
+++ b/pt-errand/target/classes/mapper/ReportMapper.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.ReportMapper">
+
+
+    <select id="getReportList" resultType="com.ruoyi.errand.object.vo.sys.ReportPageListVO">
+        select
+            tr.id as id,
+            tr.community_name as communityName,
+            tre.name as regionName,
+            tr.address_detail as addressDetail,
+            tau.name as appUserName,
+            tau.phone as appUserPhone,
+            tr.create_time as createTime,
+            tr.status as status
+            from
+                t_report tr
+        left join t_app_user tau on tr.app_user_id = tau.id
+        left join t_region tre on tr.region_id = tre.id
+        where
+            tr.del_flag=0
+        <if test="dto.communityName!=null and ''!=dto.communityName">
+            and tr.community_name like concat('%',#{dto.communityName},'%')
+        </if>
+        <if test="dto.regionId!=null">
+            and tre.id =#{dto.regionId}
+        </if>
+        <if test="dto.appUserName!=null and ''!=dto.appUserName">
+            and tau.name like concat('%',#{dto.appUserName},'%')
+        </if>
+        <if test="dto.appUserPhone!=null and ''!=dto.appUserPhone">
+            and tau.phone like concat('%',#{dto.appUserPhone},'%')
+        </if>
+        <if test="dto.status!=null">
+            and tr.status =#{dto.status}
+        </if>
+
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/SystemConfigMapper.xml b/pt-errand/target/classes/mapper/SystemConfigMapper.xml
new file mode 100644
index 0000000..5c1b2e6
--- /dev/null
+++ b/pt-errand/target/classes/mapper/SystemConfigMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.SystemConfigMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/UserCancellationLogMapper.xml b/pt-errand/target/classes/mapper/UserCancellationLogMapper.xml
new file mode 100644
index 0000000..b92b748
--- /dev/null
+++ b/pt-errand/target/classes/mapper/UserCancellationLogMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.UserCancellationLogMapper">
+
+
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/VipOrderMapper.xml b/pt-errand/target/classes/mapper/VipOrderMapper.xml
new file mode 100644
index 0000000..90d44c9
--- /dev/null
+++ b/pt-errand/target/classes/mapper/VipOrderMapper.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.VipOrderMapper">
+
+
+    <select id="selectRefundVipOrder" resultType="com.ruoyi.errand.domain.VipOrder">
+        select order_number,payment_amount
+        from t_vip_order tvo
+        left join
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/mapper/VipSettingMapper.xml b/pt-errand/target/classes/mapper/VipSettingMapper.xml
new file mode 100644
index 0000000..6e6523e
--- /dev/null
+++ b/pt-errand/target/classes/mapper/VipSettingMapper.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.errand.mapper.VipSettingMapper">
+
+
+    <select id="getVipInfoList" resultType="com.ruoyi.errand.object.vo.app.VipInfoListVO">
+        select id,vip_name,vip_price
+        from
+            t_vip_setting
+        order by id asc
+    </select>
+</mapper>
\ No newline at end of file
diff --git a/pt-errand/target/classes/templates/bjd.xlsx b/pt-errand/target/classes/templates/bjd.xlsx
new file mode 100644
index 0000000..94d32d1
--- /dev/null
+++ b/pt-errand/target/classes/templates/bjd.xlsx
Binary files differ
diff --git a/pt-errand/target/maven-archiver/pom.properties b/pt-errand/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..ff5a810
--- /dev/null
+++ b/pt-errand/target/maven-archiver/pom.properties
@@ -0,0 +1,3 @@
+artifactId=pt-errand
+groupId=com.pt
+version=3.8.9
diff --git a/pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..8d91a4b
--- /dev/null
+++ b/pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,118 @@
+com\ruoyi\bussiness\object\request\report\ReportPageRequest.class
+com\ruoyi\bussiness\domain\Banner.class
+com\ruoyi\bussiness\object\request\placementApply\ApplyProblemExportRequest.class
+com\ruoyi\bussiness\service\PlacementBatchHouseholdService.class
+com\ruoyi\bussiness\domain\Feedback.class
+com\ruoyi\bussiness\mapper\PlacementApplyMapper.class
+com\ruoyi\bussiness\mapper\DataFileMapper.class
+com\ruoyi\bussiness\mapper\PlacementErrorMapper.class
+com\ruoyi\bussiness\object\response\screen\ImportErrorResponse.class
+com\ruoyi\bussiness\service\PlacementService.class
+com\ruoyi\bussiness\domain\VipSetting.class
+com\ruoyi\bussiness\object\response\placement\PlacementPageResponse.class
+com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchDetailRequest.class
+com\ruoyi\bussiness\domain\Agreement.class
+com\ruoyi\bussiness\object\response\report\ReportSumResponse.class
+com\ruoyi\bussiness\service\ReportService.class
+com\ruoyi\bussiness\domain\PlacementBatchHousehold.class
+com\ruoyi\bussiness\object\request\placementApply\PlacementImportsRequest.class
+com\ruoyi\bussiness\object\request\placementApply\PlacementApplyRecordPageRequest.class
+com\ruoyi\bussiness\utils\BatchNumberUtils.class
+com\ruoyi\bussiness\service\CompensateService.class
+com\ruoyi\bussiness\service\PlacementBatchAssetService.class
+com\ruoyi\bussiness\object\response\screen\MonthCompensationResponse.class
+com\ruoyi\bussiness\enums\MessageTypeEnum.class
+com\ruoyi\bussiness\mapper\PlacementMapper.class
+com\ruoyi\bussiness\object\request\placementApply\PlacementApplyIdRequest.class
+com\ruoyi\bussiness\service\PlacementErrorService.class
+com\ruoyi\bussiness\domain\Region.class
+com\ruoyi\bussiness\object\response\placementApply\ProblemExportResponse.class
+com\ruoyi\bussiness\service\impl\PlacementServiceImpl.class
+com\ruoyi\bussiness\object\response\placementApply\PlacementApplyTemplateResponse.class
+com\ruoyi\bussiness\object\response\screen\PlacementTypeResponse.class
+com\ruoyi\bussiness\object\response\screen\ScreenResponse.class
+com\ruoyi\bussiness\object\request\report\DetailPageRequest.class
+com\ruoyi\bussiness\domain\PlacementError.class
+com\ruoyi\bussiness\object\response\placementApply\PlacementApplyRecordIdRequest.class
+com\ruoyi\bussiness\object\request\placementApply\ApplyAllExportRequest.class
+com\ruoyi\bussiness\object\response\placementBatch\AssetExportResponse.class
+com\ruoyi\bussiness\service\impl\PlacementBatchHouseholdServiceImpl.class
+com\ruoyi\bussiness\service\impl\PlacementApplyRecordServiceImpl.class
+com\ruoyi\bussiness\object\request\placementBatch\ProblemExportRequest.class
+com\ruoyi\bussiness\object\response\screen\QuarterProcessResponse.class
+com\ruoyi\bussiness\domain\PlacementApply.class
+com\ruoyi\bussiness\domain\Community.class
+com\ruoyi\bussiness\service\impl\DataFileServiceImpl.class
+com\ruoyi\bussiness\service\impl\CompensateServiceImpl.class
+com\ruoyi\bussiness\object\request\placement\PlacementPageRequest.class
+com\ruoyi\bussiness\object\response\placementBatch\PlacementAssetTemplateResponse.class
+com\ruoyi\bussiness\object\response\placementApply\PlacementApplyPageResponse.class
+com\ruoyi\bussiness\object\request\error\ErrorPageRequest.class
+com\ruoyi\bussiness\object\response\placementBatch\PlacementBatchPageResponse.class
+com\ruoyi\bussiness\object\response\error\ErrorPageResponse.class
+com\ruoyi\bussiness\service\PlacementBatchService.class
+com\ruoyi\bussiness\domain\Placement.class
+com\ruoyi\bussiness\enums\AppUserStatusEnum.class
+com\ruoyi\bussiness\object\request\placementApply\PlacementApplyPageRequest.class
+com\ruoyi\bussiness\domain\Courier.class
+com\ruoyi\bussiness\service\PlacementApplyService.class
+com\ruoyi\bussiness\service\PlacementApplyRecordService.class
+com\ruoyi\bussiness\object\request\dataFile\FileDelRequest.class
+com\ruoyi\bussiness\mapper\PlacementApplyRecordMapper.class
+com\ruoyi\bussiness\domain\Phone.class
+com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchIdRequest.class
+com\ruoyi\bussiness\object\response\screen\StreetResponse.class
+com\ruoyi\bussiness\object\response\placementBatch\PlacementHouseholdTemplateResponse.class
+com\ruoyi\bussiness\object\response\report\DetailResponse.class
+com\ruoyi\bussiness\service\DataFileService.class
+com\ruoyi\bussiness\domain\PlacementBatch.class
+com\ruoyi\bussiness\object\response\screen\MapResponse.class
+com\ruoyi\bussiness\object\response\placement\PlacementTemplateResponse.class
+com\ruoyi\bussiness\object\response\report\DetailPageResponse.class
+com\ruoyi\bussiness\object\request\dataFile\ListRequest.class
+com\ruoyi\bussiness\enums\DelFlagEnum.class
+com\ruoyi\bussiness\object\request\dataFile\FileUpdateRequest.class
+com\ruoyi\bussiness\utils\PaymentCycleHelper.class
+com\ruoyi\bussiness\object\response\screen\SelfBuyResponse.class
+com\ruoyi\bussiness\mapper\PlacementBatchHouseholdMapper.class
+com\ruoyi\bussiness\service\impl\PlacementBatchServiceImpl.class
+com\ruoyi\bussiness\domain\CommunityCourier.class
+com\ruoyi\bussiness\object\request\report\ReportRequest.class
+com\ruoyi\bussiness\domain\Compensate.class
+com\ruoyi\bussiness\domain\UserCancellationLog.class
+com\ruoyi\bussiness\object\request\placementBatch\ApproveRequest.class
+com\ruoyi\bussiness\domain\PlacementApplyRecord.class
+com\ruoyi\bussiness\object\request\placement\PlacementImportRequest.class
+com\ruoyi\bussiness\object\response\placementApply\PlacementApplyRecordPageResponse.class
+com\ruoyi\bussiness\service\impl\ScreenServiceImpl.class
+com\ruoyi\bussiness\domain\AddressBook.class
+com\ruoyi\bussiness\service\impl\PlacementErrorServiceImpl.class
+com\ruoyi\bussiness\object\response\placementBatch\HouseholdExportResponse.class
+com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchImportRequest.class
+com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchPageRequest.class
+com\ruoyi\bussiness\mapper\PlacementBatchMapper.class
+com\ruoyi\bussiness\object\request\placement\PlacementIdRequest.class
+com\ruoyi\bussiness\object\response\report\ReportPageResponse.class
+com\ruoyi\bussiness\object\request\placementApply\PlacementApplyApproveRequest.class
+com\ruoyi\bussiness\object\response\report\ReportResponse.class
+com\ruoyi\bussiness\object\response\placementBatch\PlacementBatchDetailResponse.class
+com\ruoyi\bussiness\domain\PlacementBatchAsset.class
+com\ruoyi\bussiness\object\response\report\ReportExportResponse.class
+com\ruoyi\bussiness\enums\TemplateFileTypeEnum.class
+com\ruoyi\bussiness\mapper\CompensateMapper.class
+com\ruoyi\bussiness\object\request\dataFile\FileAddRequest.class
+com\ruoyi\bussiness\domain\AppUser.class
+com\ruoyi\bussiness\object\response\dataFile\DataFileResponse.class
+com\ruoyi\bussiness\service\impl\PlacementBatchAssetServiceImpl.class
+com\ruoyi\bussiness\enums\CompensateEnum.class
+com\ruoyi\bussiness\service\ScreenService.class
+com\ruoyi\bussiness\mapper\PlacementBatchAssetMapper.class
+com\ruoyi\bussiness\object\request\placementBatch\IdRequest.class
+com\ruoyi\bussiness\domain\SystemConfig.class
+com\ruoyi\bussiness\object\request\report\DetailExportsRequest.class
+com\ruoyi\bussiness\object\response\placement\GetHouseHistoryRequest.class
+com\ruoyi\bussiness\service\impl\PlacementApplyServiceImpl.class
+com\ruoyi\bussiness\service\impl\ReportServiceImpl.class
+com\ruoyi\bussiness\domain\DataFile.class
+com\ruoyi\bussiness\object\vo\login\LoginVO.class
+com\ruoyi\bussiness\object\response\dataFile\ListResponse.class
diff --git a/pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..fa53a27
--- /dev/null
+++ b/pt-errand/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,102 @@
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\report\ReportPageRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\PlacementBatchAssetMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\dataFile\FileDelRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\dataFile\FileUpdateRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementApply\ProblemExportResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\PlacementBatchHouseholdService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\PlacementBatchHouseholdMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementBatch\PlacementBatchDetailResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\PlacementBatchService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\Compensate.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\report\DetailPageResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\PlacementBatchAssetServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\StreetResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\dataFile\FileAddRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\PlacementErrorMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\MapResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementApply\PlacementApplyTemplateResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementBatch\HouseholdExportResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\PlacementBatchMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementBatch\ApproveRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\report\ReportResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\dataFile\DataFileResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\DataFileMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementBatch\PlacementAssetTemplateResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementBatch\PlacementBatchPageResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\PlacementTypeResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchImportRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementBatch\PlacementHouseholdTemplateResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementBatch\AssetExportResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementApply\PlacementImportsRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\enums\TemplateFileTypeEnum.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\PlacementService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\Placement.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\PlacementApplyRecordServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\PlacementApply.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\PlacementError.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\PlacementServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\ScreenService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\PlacementBatchAsset.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementApply\PlacementApplyApproveRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\ScreenResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\PlacementBatchServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementApply\ApplyAllExportRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchDetailRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\ReportServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementBatch\IdRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementApply\PlacementApplyRecordPageRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\DataFileService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\report\DetailExportsRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\CompensateMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementApply\PlacementApplyRecordPageResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\PlacementApplyRecordMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\MonthCompensationResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\CompensateServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\DataFileServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\error\ErrorPageRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\dataFile\ListResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\PlacementBatchHouseholdServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\PlacementApplyRecord.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\dataFile\ListRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\report\DetailResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\CompensateService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\utils\PaymentCycleHelper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\enums\CompensateEnum.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\report\ReportSumResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\ReportService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\PlacementErrorService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\utils\BatchNumberUtils.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\ScreenServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\PlacementBatchAssetService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\report\DetailPageRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\DataFile.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\report\ReportPageResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\PlacementMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placement\PlacementImportRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\error\ErrorPageResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placement\GetHouseHistoryRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\PlacementBatch.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementApply\PlacementApplyIdRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementApply\PlacementApplyPageResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placement\PlacementPageResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\SelfBuyResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\mapper\PlacementApplyMapper.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\PlacementApplyServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placementApply\PlacementApplyRecordIdRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementApply\ApplyProblemExportRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\domain\PlacementBatchHousehold.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchPageRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\impl\PlacementErrorServiceImpl.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\report\ReportExportResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\PlacementApplyRecordService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\placement\PlacementTemplateResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\ImportErrorResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placement\PlacementIdRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementBatch\ProblemExportRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placement\PlacementPageRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\enums\MessageTypeEnum.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementApply\PlacementApplyPageRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\response\screen\QuarterProcessResponse.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\placementBatch\PlacementBatchIdRequest.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\service\PlacementApplyService.java
+E:\hlg资料\项目\paotui\pt-bussiness\src\main\java\com\ruoyi\bussiness\object\request\report\ReportRequest.java
diff --git a/pt-framework/pom.xml b/pt-framework/pom.xml
new file mode 100644
index 0000000..a2e6b89
--- /dev/null
+++ b/pt-framework/pom.xml
@@ -0,0 +1,68 @@
+<?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-framework</artifactId>
+
+    <description>
+        framework框架核心
+    </description>
+
+    <dependencies>
+
+        <!-- SpringBoot Web容器 -->
+         <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <!-- SpringBoot 拦截器 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <!-- 阿里数据库连接池 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>pro.fessional</groupId>
+            <artifactId>kaptcha</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>servlet-api</artifactId>
+                    <groupId>javax.servlet</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- 获取系统信息 -->
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+        </dependency>
+
+        <!-- 系统模块-->
+        <dependency>
+            <groupId>com.pt</groupId>
+            <artifactId>pt-system</artifactId>
+        </dependency>
+        <!-- 用户模块 -->
+        <dependency>
+            <groupId>com.pt</groupId>
+            <artifactId>pt-errand</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
new file mode 100644
index 0000000..b2337c9
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
@@ -0,0 +1,184 @@
+package com.ruoyi.framework.aspectj;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.BaseEntity;
+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.text.Convert;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.security.context.PermissionContextHolder;
+
+/**
+ * 数据过滤处理
+ *
+ * @author ruoyi
+ */
+@Aspect
+@Component
+public class DataScopeAspect
+{
+    /**
+     * 全部数据权限
+     */
+    public static final String DATA_SCOPE_ALL = "1";
+
+    /**
+     * 自定数据权限
+     */
+    public static final String DATA_SCOPE_CUSTOM = "2";
+
+    /**
+     * 部门数据权限
+     */
+    public static final String DATA_SCOPE_DEPT = "3";
+
+    /**
+     * 部门及以下数据权限
+     */
+    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
+
+    /**
+     * 仅本人数据权限
+     */
+    public static final String DATA_SCOPE_SELF = "5";
+
+    /**
+     * 数据权限过滤关键字
+     */
+    public static final String DATA_SCOPE = "dataScope";
+
+    @Before("@annotation(controllerDataScope)")
+    public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
+    {
+        clearDataScope(point);
+        handleDataScope(point, controllerDataScope);
+    }
+
+    protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
+    {
+        // 获取当前的用户
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (StringUtils.isNotNull(loginUser))
+        {
+            SysUser currentUser = loginUser.getUser();
+            // 如果是超级管理员,则不过滤数据
+            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
+            {
+                String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
+                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
+            }
+        }
+    }
+
+    /**
+     * 数据范围过滤
+     *
+     * @param joinPoint 切点
+     * @param user 用户
+     * @param deptAlias 部门别名
+     * @param userAlias 用户别名
+     * @param permission 权限字符
+     */
+    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)
+    {
+        StringBuilder sqlString = new StringBuilder();
+        List<String> conditions = new ArrayList<String>();
+        List<String> scopeCustomIds = new ArrayList<String>();
+        user.getRoles().forEach(role -> {
+            if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
+            {
+                scopeCustomIds.add(Convert.toStr(role.getRoleId()));
+            }
+        });
+
+        for (SysRole role : user.getRoles())
+        {
+            String dataScope = role.getDataScope();
+            if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
+            {
+                continue;
+            }
+            if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
+            {
+                continue;
+            }
+            if (DATA_SCOPE_ALL.equals(dataScope))
+            {
+                sqlString = new StringBuilder();
+                conditions.add(dataScope);
+                break;
+            }
+            else if (DATA_SCOPE_CUSTOM.equals(dataScope))
+            {
+                if (scopeCustomIds.size() > 1)
+                {
+                    // 多个自定数据权限使用in查询,避免多次拼接。
+                    sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
+                }
+                else
+                {
+                    sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
+                }
+            }
+            else if (DATA_SCOPE_DEPT.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
+            }
+            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
+            {
+                sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
+            }
+            else if (DATA_SCOPE_SELF.equals(dataScope))
+            {
+                if (StringUtils.isNotBlank(userAlias))
+                {
+                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
+                }
+                else
+                {
+                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
+                    sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
+                }
+            }
+            conditions.add(dataScope);
+        }
+
+        // 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
+        if (StringUtils.isEmpty(conditions))
+        {
+            sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
+        }
+
+        if (StringUtils.isNotBlank(sqlString.toString()))
+        {
+            Object params = joinPoint.getArgs()[0];
+            if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
+            {
+                BaseEntity baseEntity = (BaseEntity) params;
+                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
+            }
+        }
+    }
+
+    /**
+     * 拼接权限sql前先清空params.dataScope参数防止注入
+     */
+    private void clearDataScope(final JoinPoint joinPoint)
+    {
+        Object params = joinPoint.getArgs()[0];
+        if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
+        {
+            BaseEntity baseEntity = (BaseEntity) params;
+            baseEntity.getParams().put(DATA_SCOPE, "");
+        }
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java
new file mode 100644
index 0000000..8c2c9f4
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java
@@ -0,0 +1,72 @@
+package com.ruoyi.framework.aspectj;
+
+import java.util.Objects;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.annotation.DataSource;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;
+
+/**
+ * 多数据源处理
+ * 
+ * @author ruoyi
+ */
+@Aspect
+@Order(1)
+@Component
+public class DataSourceAspect
+{
+    protected Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
+            + "|| @within(com.ruoyi.common.annotation.DataSource)")
+    public void dsPointCut()
+    {
+
+    }
+
+    @Around("dsPointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable
+    {
+        DataSource dataSource = getDataSource(point);
+
+        if (StringUtils.isNotNull(dataSource))
+        {
+            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
+        }
+
+        try
+        {
+            return point.proceed();
+        }
+        finally
+        {
+            // 销毁数据源 在执行方法之后
+            DynamicDataSourceContextHolder.clearDataSourceType();
+        }
+    }
+
+    /**
+     * 获取需要切换的数据源
+     */
+    public DataSource getDataSource(ProceedingJoinPoint point)
+    {
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
+        if (Objects.nonNull(dataSource))
+        {
+            return dataSource;
+        }
+
+        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
new file mode 100644
index 0000000..bacf820
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
@@ -0,0 +1,256 @@
+package com.ruoyi.framework.aspectj;
+
+import java.util.Collection;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.ArrayUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.enums.BusinessStatus;
+import com.ruoyi.common.enums.HttpMethod;
+import com.ruoyi.common.filter.PropertyPreExcludeFilter;
+import com.ruoyi.common.utils.ExceptionUtil;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.ip.IpUtils;
+import com.ruoyi.framework.manager.AsyncManager;
+import com.ruoyi.framework.manager.factory.AsyncFactory;
+import com.ruoyi.system.domain.SysOperLog;
+
+/**
+ * 操作日志记录处理
+ * 
+ * @author ruoyi
+ */
+@Aspect
+@Component
+public class LogAspect
+{
+    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
+
+    /** 排除敏感属性字段 */
+    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
+
+    /** 计算操作消耗时间 */
+    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
+
+    /**
+     * 处理请求前执行
+     */
+    @Before(value = "@annotation(controllerLog)")
+    public void doBefore(JoinPoint joinPoint, Log controllerLog)
+    {
+        TIME_THREADLOCAL.set(System.currentTimeMillis());
+    }
+
+    /**
+     * 处理完请求后执行
+     *
+     * @param joinPoint 切点
+     */
+    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
+    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
+    {
+        handleLog(joinPoint, controllerLog, null, jsonResult);
+    }
+
+    /**
+     * 拦截异常操作
+     * 
+     * @param joinPoint 切点
+     * @param e 异常
+     */
+    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
+    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
+    {
+        handleLog(joinPoint, controllerLog, e, null);
+    }
+
+    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
+    {
+        try
+        {
+            // 获取当前的用户
+            LoginUser loginUser = SecurityUtils.getLoginUser();
+
+            // *========数据库日志=========*//
+            SysOperLog operLog = new SysOperLog();
+            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
+            // 请求的地址
+            String ip = IpUtils.getIpAddr();
+            operLog.setOperIp(ip);
+            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
+            if (loginUser != null)
+            {
+                operLog.setOperName(loginUser.getUsername());
+                SysUser currentUser = loginUser.getUser();
+                if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept()))
+                {
+                    operLog.setDeptName(currentUser.getDept().getDeptName());
+                }
+            }
+
+            if (e != null)
+            {
+                operLog.setStatus(BusinessStatus.FAIL.ordinal());
+                operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000));
+            }
+            // 设置方法名称
+            String className = joinPoint.getTarget().getClass().getName();
+            String methodName = joinPoint.getSignature().getName();
+            operLog.setMethod(className + "." + methodName + "()");
+            // 设置请求方式
+            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
+            // 处理设置注解上的参数
+            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
+            // 设置消耗时间
+            operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
+            // 保存数据库
+            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
+        }
+        catch (Exception exp)
+        {
+            // 记录本地异常日志
+            log.error("异常信息:{}", exp.getMessage());
+            exp.printStackTrace();
+        }
+        finally
+        {
+            TIME_THREADLOCAL.remove();
+        }
+    }
+
+    /**
+     * 获取注解中对方法的描述信息 用于Controller层注解
+     * 
+     * @param log 日志
+     * @param operLog 操作日志
+     * @throws Exception
+     */
+    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
+    {
+        // 设置action动作
+        operLog.setBusinessType(log.businessType().ordinal());
+        // 设置标题
+        operLog.setTitle(log.title());
+        // 设置操作人类别
+        operLog.setOperatorType(log.operatorType().ordinal());
+        // 是否需要保存request,参数和值
+        if (log.isSaveRequestData())
+        {
+            // 获取参数的信息,传入到数据库中。
+            setRequestValue(joinPoint, operLog, log.excludeParamNames());
+        }
+        // 是否需要保存response,参数和值
+        if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
+        {
+            operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
+        }
+    }
+
+    /**
+     * 获取请求的参数,放到log中
+     * 
+     * @param operLog 操作日志
+     * @throws Exception 异常
+     */
+    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
+    {
+        Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
+        String requestMethod = operLog.getRequestMethod();
+        if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
+        {
+            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
+            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
+        }
+        else
+        {
+            operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
+        }
+    }
+
+    /**
+     * 参数拼装
+     */
+    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
+    {
+        String params = "";
+        if (paramsArray != null && paramsArray.length > 0)
+        {
+            for (Object o : paramsArray)
+            {
+                if (StringUtils.isNotNull(o) && !isFilterObject(o))
+                {
+                    try
+                    {
+                        String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
+                        params += jsonObj.toString() + " ";
+                    }
+                    catch (Exception e)
+                    {
+                    }
+                }
+            }
+        }
+        return params.trim();
+    }
+
+    /**
+     * 忽略敏感属性
+     */
+    public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames)
+    {
+        return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
+    }
+
+    /**
+     * 判断是否需要过滤的对象。
+     * 
+     * @param o 对象信息。
+     * @return 如果是需要过滤的对象,则返回true;否则返回false。
+     */
+    @SuppressWarnings("rawtypes")
+    public boolean isFilterObject(final Object o)
+    {
+        Class<?> clazz = o.getClass();
+        if (clazz.isArray())
+        {
+            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+        }
+        else if (Collection.class.isAssignableFrom(clazz))
+        {
+            Collection collection = (Collection) o;
+            for (Object value : collection)
+            {
+                return value instanceof MultipartFile;
+            }
+        }
+        else if (Map.class.isAssignableFrom(clazz))
+        {
+            Map map = (Map) o;
+            for (Object value : map.entrySet())
+            {
+                Map.Entry entry = (Map.Entry) value;
+                return entry.getValue() instanceof MultipartFile;
+            }
+        }
+        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+                || o instanceof BindingResult;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
new file mode 100644
index 0000000..b720bc1
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
@@ -0,0 +1,89 @@
+package com.ruoyi.framework.aspectj;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.annotation.RateLimiter;
+import com.ruoyi.common.enums.LimitType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.ip.IpUtils;
+
+/**
+ * 限流处理
+ *
+ * @author ruoyi
+ */
+@Aspect
+@Component
+public class RateLimiterAspect
+{
+    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
+
+    private RedisTemplate<Object, Object> redisTemplate;
+
+    private RedisScript<Long> limitScript;
+
+    @Autowired
+    public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
+    {
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Autowired
+    public void setLimitScript(RedisScript<Long> limitScript)
+    {
+        this.limitScript = limitScript;
+    }
+
+    @Before("@annotation(rateLimiter)")
+    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
+    {
+        int time = rateLimiter.time();
+        int count = rateLimiter.count();
+
+        String combineKey = getCombineKey(rateLimiter, point);
+        List<Object> keys = Collections.singletonList(combineKey);
+        try
+        {
+            Long number = redisTemplate.execute(limitScript, keys, count, time);
+            if (StringUtils.isNull(number) || number.intValue() > count)
+            {
+                throw new ServiceException("访问过于频繁,请稍候再试");
+            }
+            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);
+        }
+        catch (ServiceException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException("服务器限流异常,请稍候再试");
+        }
+    }
+
+    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
+    {
+        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
+        if (rateLimiter.limitType() == LimitType.IP)
+        {
+            stringBuffer.append(IpUtils.getIpAddr()).append("-");
+        }
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        Class<?> targetClass = method.getDeclaringClass();
+        stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
+        return stringBuffer.toString();
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java
new file mode 100644
index 0000000..1d4dc1f
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java
@@ -0,0 +1,30 @@
+package com.ruoyi.framework.config;
+
+import java.util.TimeZone;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+/**
+ * 程序注解配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+// 表示通过aop框架暴露该代理对象,AopContext能够访问
+@EnableAspectJAutoProxy(exposeProxy = true)
+// 指定要扫描的Mapper类的包的路径
+@MapperScan("com.ruoyi.**.mapper")
+public class ApplicationConfig
+{
+    /**
+     * 时区配置
+     */
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
+    {
+        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java
new file mode 100644
index 0000000..43e78ae
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java
@@ -0,0 +1,83 @@
+package com.ruoyi.framework.config;
+
+import java.util.Properties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import static com.google.code.kaptcha.Constants.*;
+
+/**
+ * 验证码配置
+ * 
+ * @author ruoyi
+ */
+@Configuration
+public class CaptchaConfig
+{
+    @Bean(name = "captchaProducer")
+    public DefaultKaptcha getKaptchaBean()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+
+    @Bean(name = "captchaProducerMath")
+    public DefaultKaptcha getKaptchaBeanMath()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 边框颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
+        // 验证码文本生成器
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator");
+        // 验证码文本字符间距 默认为2
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 验证码噪点颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
+        // 干扰实现类
+        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java
new file mode 100644
index 0000000..f6abac1
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java
@@ -0,0 +1,126 @@
+package com.ruoyi.framework.config;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.sql.DataSource;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.util.Utils;
+import com.ruoyi.common.enums.DataSourceType;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.framework.config.properties.DruidProperties;
+import com.ruoyi.framework.datasource.DynamicDataSource;
+
+/**
+ * druid 配置多数据源
+ * 
+ * @author ruoyi
+ */
+@Configuration
+public class DruidConfig
+{
+    @Bean
+    @ConfigurationProperties("spring.datasource.druid.master")
+    public DataSource masterDataSource(DruidProperties druidProperties)
+    {
+        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+        return druidProperties.dataSource(dataSource);
+    }
+
+    @Bean
+    @ConfigurationProperties("spring.datasource.druid.slave")
+    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
+    public DataSource slaveDataSource(DruidProperties druidProperties)
+    {
+        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+        return druidProperties.dataSource(dataSource);
+    }
+
+    @Bean(name = "dynamicDataSource")
+    @Primary
+    public DynamicDataSource dataSource(DataSource masterDataSource)
+    {
+        Map<Object, Object> targetDataSources = new HashMap<>();
+        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
+        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
+        return new DynamicDataSource(masterDataSource, targetDataSources);
+    }
+    
+    /**
+     * 设置数据源
+     * 
+     * @param targetDataSources 备选数据源集合
+     * @param sourceName 数据源名称
+     * @param beanName bean名称
+     */
+    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
+    {
+        try
+        {
+            DataSource dataSource = SpringUtils.getBean(beanName);
+            targetDataSources.put(sourceName, dataSource);
+        }
+        catch (Exception e)
+        {
+        }
+    }
+
+    /**
+     * 去除监控页面底部的广告
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
+    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
+    {
+        // 获取web监控页面的参数
+        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
+        // 提取common.js的配置路径
+        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
+        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
+        final String filePath = "support/http/resources/js/common.js";
+        // 创建filter进行过滤
+        Filter filter = new Filter()
+        {
+            @Override
+            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
+            {
+            }
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+                    throws IOException, ServletException
+            {
+                chain.doFilter(request, response);
+                // 重置缓冲区,响应头不会被重置
+                response.resetBuffer();
+                // 获取common.js
+                String text = Utils.readFromResource(filePath);
+                // 正则替换banner, 除去底部的广告信息
+                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
+                text = text.replaceAll("powered.*?shrek.wang</a>", "");
+                response.getWriter().write(text);
+            }
+            @Override
+            public void destroy()
+            {
+            }
+        };
+        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+        registrationBean.setFilter(filter);
+        registrationBean.addUrlPatterns(commonJsPattern);
+        return registrationBean;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/pt-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java
new file mode 100644
index 0000000..4adbb7f
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java
@@ -0,0 +1,52 @@
+package com.ruoyi.framework.config;
+
+import java.nio.charset.Charset;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.SerializationException;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONReader;
+import com.alibaba.fastjson2.JSONWriter;
+import com.alibaba.fastjson2.filter.Filter;
+import com.ruoyi.common.constant.Constants;
+
+/**
+ * Redis使用FastJson序列化
+ * 
+ * @author ruoyi
+ */
+public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
+{
+    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
+
+    static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR);
+
+    private Class<T> clazz;
+
+    public FastJson2JsonRedisSerializer(Class<T> clazz)
+    {
+        super();
+        this.clazz = clazz;
+    }
+
+    @Override
+    public byte[] serialize(T t) throws SerializationException
+    {
+        if (t == null)
+        {
+            return new byte[0];
+        }
+        return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
+    }
+
+    @Override
+    public T deserialize(byte[] bytes) throws SerializationException
+    {
+        if (bytes == null || bytes.length <= 0)
+        {
+            return null;
+        }
+        String str = new String(bytes, DEFAULT_CHARSET);
+
+        return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER);
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java
new file mode 100644
index 0000000..bb14c04
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java
@@ -0,0 +1,58 @@
+package com.ruoyi.framework.config;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.DispatcherType;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.ruoyi.common.filter.RepeatableFilter;
+import com.ruoyi.common.filter.XssFilter;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * Filter配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+public class FilterConfig
+{
+    @Value("${xss.excludes}")
+    private String excludes;
+
+    @Value("${xss.urlPatterns}")
+    private String urlPatterns;
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
+    public FilterRegistrationBean xssFilterRegistration()
+    {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setDispatcherTypes(DispatcherType.REQUEST);
+        registration.setFilter(new XssFilter());
+        registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
+        registration.setName("xssFilter");
+        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
+        Map<String, String> initParameters = new HashMap<String, String>();
+        initParameters.put("excludes", excludes);
+        registration.setInitParameters(initParameters);
+        return registration;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Bean
+    public FilterRegistrationBean someFilterRegistration()
+    {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setFilter(new RepeatableFilter());
+        registration.addUrlPatterns("/*");
+        registration.setName("repeatableFilter");
+        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
+        return registration;
+    }
+
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java
new file mode 100644
index 0000000..163fd01
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java
@@ -0,0 +1,43 @@
+package com.ruoyi.framework.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+import org.springframework.web.servlet.i18n.SessionLocaleResolver;
+import com.ruoyi.common.constant.Constants;
+
+/**
+ * 资源文件配置加载
+ * 
+ * @author ruoyi
+ */
+@Configuration
+public class I18nConfig implements WebMvcConfigurer
+{
+    @Bean
+    public LocaleResolver localeResolver()
+    {
+        SessionLocaleResolver slr = new SessionLocaleResolver();
+        // 默认语言
+        slr.setDefaultLocale(Constants.DEFAULT_LOCALE);
+        return slr;
+    }
+
+    @Bean
+    public LocaleChangeInterceptor localeChangeInterceptor()
+    {
+        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
+        // 参数名
+        lci.setParamName("lang");
+        return lci;
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry)
+    {
+        registry.addInterceptor(localeChangeInterceptor());
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java b/pt-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java
new file mode 100644
index 0000000..7f8e1d5
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java
@@ -0,0 +1,68 @@
+package com.ruoyi.framework.config;
+
+import java.util.Random;
+import com.google.code.kaptcha.text.impl.DefaultTextCreator;
+
+/**
+ * 验证码文本生成器
+ *
+ * @author ruoyi
+ */
+public class KaptchaTextCreator extends DefaultTextCreator
+{
+    private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
+
+    @Override
+    public String getText()
+    {
+        Integer result = 0;
+        Random random = new Random();
+        int x = random.nextInt(10);
+        int y = random.nextInt(10);
+        StringBuilder suChinese = new StringBuilder();
+        int randomoperands = random.nextInt(3);
+        if (randomoperands == 0)
+        {
+            result = x * y;
+            suChinese.append(CNUMBERS[x]);
+            suChinese.append("*");
+            suChinese.append(CNUMBERS[y]);
+        }
+        else if (randomoperands == 1)
+        {
+            if ((x != 0) && y % x == 0)
+            {
+                result = y / x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("/");
+                suChinese.append(CNUMBERS[x]);
+            }
+            else
+            {
+                result = x + y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("+");
+                suChinese.append(CNUMBERS[y]);
+            }
+        }
+        else
+        {
+            if (x >= y)
+            {
+                result = x - y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[y]);
+            }
+            else
+            {
+                result = y - x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[x]);
+            }
+        }
+        suChinese.append("=?@" + result);
+        return suChinese.toString();
+    }
+}
\ No newline at end of file
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java
new file mode 100644
index 0000000..bc6618a
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java
@@ -0,0 +1,132 @@
+//package com.ruoyi.framework.config;
+//
+//import java.io.IOException;
+//import java.util.ArrayList;
+//import java.util.Arrays;
+//import java.util.HashSet;
+//import java.util.List;
+//import javax.sql.DataSource;
+//import org.apache.ibatis.io.VFS;
+//import org.apache.ibatis.session.SqlSessionFactory;
+//import org.mybatis.spring.SqlSessionFactoryBean;
+//import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.core.env.Environment;
+//import org.springframework.core.io.DefaultResourceLoader;
+//import org.springframework.core.io.Resource;
+//import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+//import org.springframework.core.io.support.ResourcePatternResolver;
+//import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+//import org.springframework.core.type.classreading.MetadataReader;
+//import org.springframework.core.type.classreading.MetadataReaderFactory;
+//import org.springframework.util.ClassUtils;
+//import com.ruoyi.common.utils.StringUtils;
+//
+///**
+// * Mybatis支持*匹配扫描包
+// *
+// * @author ruoyi
+// */
+//@Configuration
+//public class MyBatisConfig
+//{
+//    @Autowired
+//    private Environment env;
+//
+//    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
+//
+//    public static String setTypeAliasesPackage(String typeAliasesPackage)
+//    {
+//        ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
+//        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
+//        List<String> allResult = new ArrayList<String>();
+//        try
+//        {
+//            for (String aliasesPackage : typeAliasesPackage.split(","))
+//            {
+//                List<String> result = new ArrayList<String>();
+//                aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+//                        + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
+//                Resource[] resources = resolver.getResources(aliasesPackage);
+//                if (resources != null && resources.length > 0)
+//                {
+//                    MetadataReader metadataReader = null;
+//                    for (Resource resource : resources)
+//                    {
+//                        if (resource.isReadable())
+//                        {
+//                            metadataReader = metadataReaderFactory.getMetadataReader(resource);
+//                            try
+//                            {
+//                                result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
+//                            }
+//                            catch (ClassNotFoundException e)
+//                            {
+//                                e.printStackTrace();
+//                            }
+//                        }
+//                    }
+//                }
+//                if (result.size() > 0)
+//                {
+//                    HashSet<String> hashResult = new HashSet<String>(result);
+//                    allResult.addAll(hashResult);
+//                }
+//            }
+//            if (allResult.size() > 0)
+//            {
+//                typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
+//            }
+//            else
+//            {
+//                throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
+//            }
+//        }
+//        catch (IOException e)
+//        {
+//            e.printStackTrace();
+//        }
+//        return typeAliasesPackage;
+//    }
+//
+//    public Resource[] resolveMapperLocations(String[] mapperLocations)
+//    {
+//        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
+//        List<Resource> resources = new ArrayList<Resource>();
+//        if (mapperLocations != null)
+//        {
+//            for (String mapperLocation : mapperLocations)
+//            {
+//                try
+//                {
+//                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
+//                    resources.addAll(Arrays.asList(mappers));
+//                }
+//                catch (IOException e)
+//                {
+//                    // ignore
+//                }
+//            }
+//        }
+//        return resources.toArray(new Resource[resources.size()]);
+//    }
+//
+//    @Bean
+//    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
+//    {
+//        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
+//        String mapperLocations = env.getProperty("mybatis.mapperLocations");
+//        String configLocation = env.getProperty("mybatis.configLocation");
+//        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
+//        VFS.addImplClass(SpringBootVFS.class);
+//
+//        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+//        sessionFactory.setDataSource(dataSource);
+//        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
+//        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
+//        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
+//        return sessionFactory.getObject();
+//    }
+//}
\ No newline at end of file
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
new file mode 100644
index 0000000..3f4f485
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
@@ -0,0 +1,69 @@
+package com.ruoyi.framework.config;
+
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * redis配置
+ * 
+ * @author ruoyi
+ */
+@Configuration
+@EnableCaching
+public class RedisConfig extends CachingConfigurerSupport
+{
+    @Bean
+    @SuppressWarnings(value = { "unchecked", "rawtypes" })
+    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
+    {
+        RedisTemplate<Object, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
+
+        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
+
+        // 使用StringRedisSerializer来序列化和反序列化redis的key值
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setValueSerializer(serializer);
+
+        // Hash的key也采用StringRedisSerializer的序列化方式
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(serializer);
+
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    public DefaultRedisScript<Long> limitScript()
+    {
+        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
+        redisScript.setScriptText(limitScriptText());
+        redisScript.setResultType(Long.class);
+        return redisScript;
+    }
+
+    /**
+     * 限流脚本
+     */
+    private String limitScriptText()
+    {
+        return "local key = KEYS[1]\n" +
+                "local count = tonumber(ARGV[1])\n" +
+                "local time = tonumber(ARGV[2])\n" +
+                "local current = redis.call('get', key);\n" +
+                "if current and tonumber(current) > count then\n" +
+                "    return tonumber(current);\n" +
+                "end\n" +
+                "current = redis.call('incr', key)\n" +
+                "if tonumber(current) == 1 then\n" +
+                "    redis.call('expire', key, time)\n" +
+                "end\n" +
+                "return tonumber(current);";
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
new file mode 100644
index 0000000..058ccd2
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
@@ -0,0 +1,88 @@
+package com.ruoyi.framework.config;
+
+import java.util.concurrent.TimeUnit;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.CacheControl;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import com.ruoyi.common.config.RuoYiConfig;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
+
+/**
+ * 通用配置
+ * 
+ * @author ruoyi
+ */
+@Configuration
+public class ResourcesConfig implements WebMvcConfigurer
+{
+    @Autowired
+    private RepeatSubmitInterceptor repeatSubmitInterceptor;
+
+
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        //分页插件
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+        //乐观锁
+        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+        return interceptor;
+    }
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry)
+    {
+        /** 本地文件上传路径 */
+        registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
+                .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
+
+        /** swagger配置 */
+        registry.addResourceHandler("/swagger-ui/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
+                .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
+    }
+
+    /**
+     * 自定义拦截规则
+     */
+    @Override
+    public void addInterceptors(InterceptorRegistry registry)
+    {
+        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
+    }
+
+    /**
+     * 跨域配置
+     */
+    @Bean
+    public CorsFilter corsFilter()
+    {
+        CorsConfiguration config = new CorsConfiguration();
+        // 设置访问源地址
+        config.addAllowedOriginPattern("*");
+        // 设置访问源请求头
+        config.addAllowedHeader("*");
+        // 设置访问源请求方法
+        config.addAllowedMethod("*");
+        // 有效期 1800秒
+        config.setMaxAge(1800L);
+        // 添加映射路径,拦截一切请求
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", config);
+        // 返回新的CorsFilter
+        return new CorsFilter(source);
+    }
+}
\ No newline at end of file
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
new file mode 100644
index 0000000..11766e3
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -0,0 +1,150 @@
+package com.ruoyi.framework.config;
+
+import com.ruoyi.errand.interceptor.APPJwtTokenInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.authentication.logout.LogoutFilter;
+import org.springframework.web.filter.CorsFilter;
+import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
+import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
+import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
+import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
+
+/**
+ * spring security配置
+ * 
+ * @author ruoyi
+ */
+@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
+@Configuration
+public class SecurityConfig
+{
+    /**
+     * 自定义用户认证逻辑
+     */
+    @Autowired
+    private UserDetailsService userDetailsService;
+    
+    /**
+     * 认证失败处理类
+     */
+    @Autowired
+    private AuthenticationEntryPointImpl unauthorizedHandler;
+
+    /**
+     * 退出处理类
+     */
+    @Autowired
+    private LogoutSuccessHandlerImpl logoutSuccessHandler;
+
+    /**
+     * token认证过滤器
+     */
+    @Autowired
+    private JwtAuthenticationTokenFilter authenticationTokenFilter;
+    
+    /**
+     * 跨域过滤器
+     */
+    @Autowired
+    private CorsFilter corsFilter;
+
+    /**
+     * APP端token认证过滤器
+     */
+    @Autowired
+    private APPJwtTokenInterceptor appJwtTokenInterceptor;
+
+    /**
+     * 允许匿名访问的地址
+     */
+    @Autowired
+    private PermitAllUrlProperties permitAllUrl;
+
+    /**
+     * 身份验证实现
+     */
+    @Bean
+    public AuthenticationManager authenticationManager()
+    {
+        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
+        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
+        daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
+        return new ProviderManager(daoAuthenticationProvider);
+    }
+
+    /**
+     * anyRequest          |   匹配所有请求路径
+     * access              |   SpringEl表达式结果为true时可以访问
+     * anonymous           |   匿名可以访问
+     * denyAll             |   用户不能访问
+     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
+     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
+     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
+     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
+     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
+     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
+     * permitAll           |   用户可以任意访问
+     * rememberMe          |   允许通过remember-me登录的用户访问
+     * authenticated       |   用户登录后可访问
+     */
+    @Bean
+    protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
+    {
+        return httpSecurity
+            // CSRF禁用,因为不使用session
+            .csrf(csrf -> csrf.disable())
+
+            // 禁用HTTP响应标头
+            .headers((headersCustomizer) -> {
+                headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
+            })
+            // 认证失败处理类
+            .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
+            // 基于token,所以不需要session
+            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+            // 注解标记允许匿名访问的url
+            .authorizeHttpRequests((requests) -> {
+                permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
+                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
+                requests.antMatchers("/login", "/register", "/captchaImage","/screen/data").permitAll()
+                    // 静态资源,可匿名访问
+                    .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
+                    .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/swagger-ui/").permitAll()
+                     .antMatchers("/app/**","/ws/**").permitAll()//小程序端可直接通行
+                    // 除上面外的所有请求全部需要鉴权认证
+
+                    .anyRequest().authenticated();
+            })
+            // 添加Logout filter
+            .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
+            // 添加JWT filter
+            .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
+            // 添加CORS filter
+            .addFilterBefore(appJwtTokenInterceptor, JwtAuthenticationTokenFilter.class)
+                .addFilterBefore(corsFilter, APPJwtTokenInterceptor.class)
+            .addFilterBefore(corsFilter, LogoutFilter.class)
+            .build();
+    }
+
+    /**
+     * 强散列哈希加密实现
+     */
+    @Bean
+    public BCryptPasswordEncoder bCryptPasswordEncoder()
+    {
+        return new BCryptPasswordEncoder();
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java
new file mode 100644
index 0000000..b5b7de3
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java
@@ -0,0 +1,32 @@
+package com.ruoyi.framework.config;
+
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.utils.ServletUtils;
+
+/**
+ * 服务相关配置
+ * 
+ * @author ruoyi
+ */
+@Component
+public class ServerConfig
+{
+    /**
+     * 获取完整的请求路径,包括:域名,端口,上下文访问路径
+     * 
+     * @return 服务地址
+     */
+    public String getUrl()
+    {
+        HttpServletRequest request = ServletUtils.getRequest();
+        return getDomain(request);
+    }
+
+    public static String getDomain(HttpServletRequest request)
+    {
+        StringBuffer url = request.getRequestURL();
+        String contextPath = request.getServletContext().getContextPath();
+        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/pt-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java
new file mode 100644
index 0000000..7840141
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java
@@ -0,0 +1,63 @@
+package com.ruoyi.framework.config;
+
+import com.ruoyi.common.utils.Threads;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 线程池配置
+ *
+ * @author ruoyi
+ **/
+@Configuration
+public class ThreadPoolConfig
+{
+    // 核心线程池大小
+    private int corePoolSize = 50;
+
+    // 最大可创建的线程数
+    private int maxPoolSize = 200;
+
+    // 队列最大长度
+    private int queueCapacity = 1000;
+
+    // 线程池维护线程所允许的空闲时间
+    private int keepAliveSeconds = 300;
+
+    @Bean(name = "threadPoolTaskExecutor")
+    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
+    {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setCorePoolSize(corePoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveSeconds);
+        // 线程池对拒绝任务(无线程可用)的处理策略
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        return executor;
+    }
+
+    /**
+     * 执行周期性或定时任务
+     */
+    @Bean(name = "scheduledExecutorService")
+    protected ScheduledExecutorService scheduledExecutorService()
+    {
+        return new ScheduledThreadPoolExecutor(corePoolSize,
+                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
+                new ThreadPoolExecutor.CallerRunsPolicy())
+        {
+            @Override
+            protected void afterExecute(Runnable r, Throwable t)
+            {
+                super.afterExecute(r, t);
+                Threads.printException(r, t);
+            }
+        };
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/pt-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java
new file mode 100644
index 0000000..c8a5c8a
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java
@@ -0,0 +1,89 @@
+package com.ruoyi.framework.config.properties;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import com.alibaba.druid.pool.DruidDataSource;
+
+/**
+ * druid 配置属性
+ * 
+ * @author ruoyi
+ */
+@Configuration
+public class DruidProperties
+{
+    @Value("${spring.datasource.druid.initialSize}")
+    private int initialSize;
+
+    @Value("${spring.datasource.druid.minIdle}")
+    private int minIdle;
+
+    @Value("${spring.datasource.druid.maxActive}")
+    private int maxActive;
+
+    @Value("${spring.datasource.druid.maxWait}")
+    private int maxWait;
+
+    @Value("${spring.datasource.druid.connectTimeout}")
+    private int connectTimeout;
+
+    @Value("${spring.datasource.druid.socketTimeout}")
+    private int socketTimeout;
+
+    @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
+    private int timeBetweenEvictionRunsMillis;
+
+    @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
+    private int minEvictableIdleTimeMillis;
+
+    @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
+    private int maxEvictableIdleTimeMillis;
+
+    @Value("${spring.datasource.druid.validationQuery}")
+    private String validationQuery;
+
+    @Value("${spring.datasource.druid.testWhileIdle}")
+    private boolean testWhileIdle;
+
+    @Value("${spring.datasource.druid.testOnBorrow}")
+    private boolean testOnBorrow;
+
+    @Value("${spring.datasource.druid.testOnReturn}")
+    private boolean testOnReturn;
+
+    public DruidDataSource dataSource(DruidDataSource datasource)
+    {
+        /** 配置初始化大小、最小、最大 */
+        datasource.setInitialSize(initialSize);
+        datasource.setMaxActive(maxActive);
+        datasource.setMinIdle(minIdle);
+
+        /** 配置获取连接等待超时的时间 */
+        datasource.setMaxWait(maxWait);
+        
+        /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */
+        datasource.setConnectTimeout(connectTimeout);
+        
+        /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */
+        datasource.setSocketTimeout(socketTimeout);
+
+        /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
+        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+
+        /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
+        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
+
+        /**
+         * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
+         */
+        datasource.setValidationQuery(validationQuery);
+        /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
+        datasource.setTestWhileIdle(testWhileIdle);
+        /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
+        datasource.setTestOnBorrow(testOnBorrow);
+        /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
+        datasource.setTestOnReturn(testOnReturn);
+        return datasource;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/pt-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java
new file mode 100644
index 0000000..29118fa
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java
@@ -0,0 +1,73 @@
+package com.ruoyi.framework.config.properties;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.RegExUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import com.ruoyi.common.annotation.Anonymous;
+
+/**
+ * 设置Anonymous注解允许匿名访问的url
+ * 
+ * @author ruoyi
+ */
+@Configuration
+public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware
+{
+    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
+
+    private ApplicationContext applicationContext;
+
+    private List<String> urls = new ArrayList<>();
+
+    public String ASTERISK = "*";
+
+    @Override
+    public void afterPropertiesSet()
+    {
+        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
+        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
+
+        map.keySet().forEach(info -> {
+            HandlerMethod handlerMethod = map.get(info);
+
+            // 获取方法上边的注解 替代path variable 为 *
+            Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
+            Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
+                    .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
+
+            // 获取类上边的注解, 替代path variable 为 *
+            Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
+            Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
+                    .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
+        });
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException
+    {
+        this.applicationContext = context;
+    }
+
+    public List<String> getUrls()
+    {
+        return urls;
+    }
+
+    public void setUrls(List<String> urls)
+    {
+        this.urls = urls;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java
new file mode 100644
index 0000000..e70b8cf
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java
@@ -0,0 +1,26 @@
+package com.ruoyi.framework.datasource;
+
+import java.util.Map;
+import javax.sql.DataSource;
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+
+/**
+ * 动态数据源
+ * 
+ * @author ruoyi
+ */
+public class DynamicDataSource extends AbstractRoutingDataSource
+{
+    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
+    {
+        super.setDefaultTargetDataSource(defaultTargetDataSource);
+        super.setTargetDataSources(targetDataSources);
+        super.afterPropertiesSet();
+    }
+
+    @Override
+    protected Object determineCurrentLookupKey()
+    {
+        return DynamicDataSourceContextHolder.getDataSourceType();
+    }
+}
\ No newline at end of file
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java
new file mode 100644
index 0000000..9770af6
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java
@@ -0,0 +1,45 @@
+package com.ruoyi.framework.datasource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 数据源切换处理
+ * 
+ * @author ruoyi
+ */
+public class DynamicDataSourceContextHolder
+{
+    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
+
+    /**
+     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
+     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
+     */
+    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
+
+    /**
+     * 设置数据源的变量
+     */
+    public static void setDataSourceType(String dsType)
+    {
+        log.info("切换到{}数据源", dsType);
+        CONTEXT_HOLDER.set(dsType);
+    }
+
+    /**
+     * 获得数据源的变量
+     */
+    public static String getDataSourceType()
+    {
+        return CONTEXT_HOLDER.get();
+    }
+
+    /**
+     * 清空数据源变量
+     */
+    public static void clearDataSourceType()
+    {
+        CONTEXT_HOLDER.remove();
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/pt-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
new file mode 100644
index 0000000..c49eaf4
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
@@ -0,0 +1,56 @@
+package com.ruoyi.framework.interceptor;
+
+import java.lang.reflect.Method;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.annotation.RepeatSubmit;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.ServletUtils;
+
+/**
+ * 防止重复提交拦截器
+ *
+ * @author ruoyi
+ */
+@Component
+public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
+{
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
+    {
+        if (handler instanceof HandlerMethod)
+        {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            Method method = handlerMethod.getMethod();
+            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
+            if (annotation != null)
+            {
+                if (this.isRepeatSubmit(request, annotation))
+                {
+                    AjaxResult ajaxResult = AjaxResult.error(annotation.message());
+                    ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));
+                    return false;
+                }
+            }
+            return true;
+        }
+        else
+        {
+            return true;
+        }
+    }
+
+    /**
+     * 验证是否重复提交由子类实现具体的防重复提交的规则
+     *
+     * @param request 请求信息
+     * @param annotation 防重复注解参数
+     * @return 结果
+     * @throws Exception
+     */
+    public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/pt-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
new file mode 100644
index 0000000..9dc9511
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
@@ -0,0 +1,110 @@
+package com.ruoyi.framework.interceptor.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.annotation.RepeatSubmit;
+import com.ruoyi.common.constant.CacheConstants;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.http.HttpHelper;
+import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
+
+/**
+ * 判断请求url和数据是否和上一次相同,
+ * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
+ * 
+ * @author ruoyi
+ */
+@Component
+public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
+{
+    public final String REPEAT_PARAMS = "repeatParams";
+
+    public final String REPEAT_TIME = "repeatTime";
+
+    // 令牌自定义标识
+    @Value("${token.header}")
+    private String header;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation)
+    {
+        String nowParams = "";
+        if (request instanceof RepeatedlyRequestWrapper)
+        {
+            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
+            nowParams = HttpHelper.getBodyString(repeatedlyRequest);
+        }
+
+        // body参数为空,获取Parameter的数据
+        if (StringUtils.isEmpty(nowParams))
+        {
+            nowParams = JSON.toJSONString(request.getParameterMap());
+        }
+        Map<String, Object> nowDataMap = new HashMap<String, Object>();
+        nowDataMap.put(REPEAT_PARAMS, nowParams);
+        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
+
+        // 请求地址(作为存放cache的key值)
+        String url = request.getRequestURI();
+
+        // 唯一值(没有消息头则使用请求地址)
+        String submitKey = StringUtils.trimToEmpty(request.getHeader(header));
+
+        // 唯一标识(指定key + url + 消息头)
+        String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;
+
+        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
+        if (sessionObj != null)
+        {
+            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
+            if (sessionMap.containsKey(url))
+            {
+                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
+                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval()))
+                {
+                    return true;
+                }
+            }
+        }
+        Map<String, Object> cacheMap = new HashMap<String, Object>();
+        cacheMap.put(url, nowDataMap);
+        redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
+        return false;
+    }
+
+    /**
+     * 判断参数是否相同
+     */
+    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
+    {
+        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
+        String preParams = (String) preMap.get(REPEAT_PARAMS);
+        return nowParams.equals(preParams);
+    }
+
+    /**
+     * 判断两次间隔时间
+     */
+    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval)
+    {
+        long time1 = (Long) nowMap.get(REPEAT_TIME);
+        long time2 = (Long) preMap.get(REPEAT_TIME);
+        if ((time1 - time2) < interval)
+        {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/pt-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java
new file mode 100644
index 0000000..7387a02
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java
@@ -0,0 +1,55 @@
+package com.ruoyi.framework.manager;
+
+import java.util.TimerTask;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import com.ruoyi.common.utils.Threads;
+import com.ruoyi.common.utils.spring.SpringUtils;
+
+/**
+ * 异步任务管理器
+ * 
+ * @author ruoyi
+ */
+public class AsyncManager
+{
+    /**
+     * 操作延迟10毫秒
+     */
+    private final int OPERATE_DELAY_TIME = 10;
+
+    /**
+     * 异步操作任务调度线程池
+     */
+    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
+
+    /**
+     * 单例模式
+     */
+    private AsyncManager(){}
+
+    private static AsyncManager me = new AsyncManager();
+
+    public static AsyncManager me()
+    {
+        return me;
+    }
+
+    /**
+     * 执行任务
+     * 
+     * @param task 任务
+     */
+    public void execute(TimerTask task)
+    {
+        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * 停止任务线程池
+     */
+    public void shutdown()
+    {
+        Threads.shutdownAndAwaitTermination(executor);
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/pt-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java
new file mode 100644
index 0000000..e36ca3c
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java
@@ -0,0 +1,39 @@
+package com.ruoyi.framework.manager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import javax.annotation.PreDestroy;
+
+/**
+ * 确保应用退出时能关闭后台线程
+ *
+ * @author ruoyi
+ */
+@Component
+public class ShutdownManager
+{
+    private static final Logger logger = LoggerFactory.getLogger("sys-user");
+
+    @PreDestroy
+    public void destroy()
+    {
+        shutdownAsyncManager();
+    }
+
+    /**
+     * 停止异步执行任务
+     */
+    private void shutdownAsyncManager()
+    {
+        try
+        {
+            logger.info("====关闭后台任务任务线程池====");
+            AsyncManager.me().shutdown();
+        }
+        catch (Exception e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/pt-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
new file mode 100644
index 0000000..267e305
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
@@ -0,0 +1,102 @@
+package com.ruoyi.framework.manager.factory;
+
+import java.util.TimerTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.utils.LogUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.ip.AddressUtils;
+import com.ruoyi.common.utils.ip.IpUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.system.domain.SysLogininfor;
+import com.ruoyi.system.domain.SysOperLog;
+import com.ruoyi.system.service.ISysLogininforService;
+import com.ruoyi.system.service.ISysOperLogService;
+import eu.bitwalker.useragentutils.UserAgent;
+
+/**
+ * 异步工厂(产生任务用)
+ * 
+ * @author ruoyi
+ */
+public class AsyncFactory
+{
+    private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
+
+    /**
+     * 记录登录信息
+     * 
+     * @param username 用户名
+     * @param status 状态
+     * @param message 消息
+     * @param args 列表
+     * @return 任务task
+     */
+    public static TimerTask recordLogininfor(final String username, final String status, final String message,
+            final Object... args)
+    {
+        final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
+        final String ip = IpUtils.getIpAddr();
+        return new TimerTask()
+        {
+            @Override
+            public void run()
+            {
+                String address = AddressUtils.getRealAddressByIP(ip);
+                StringBuilder s = new StringBuilder();
+                s.append(LogUtils.getBlock(ip));
+                s.append(address);
+                s.append(LogUtils.getBlock(username));
+                s.append(LogUtils.getBlock(status));
+                s.append(LogUtils.getBlock(message));
+                // 打印信息到日志
+                sys_user_logger.info(s.toString(), args);
+                // 获取客户端操作系统
+                String os = userAgent.getOperatingSystem().getName();
+                // 获取客户端浏览器
+                String browser = userAgent.getBrowser().getName();
+                // 封装对象
+                SysLogininfor logininfor = new SysLogininfor();
+                logininfor.setUserName(username);
+                logininfor.setIpaddr(ip);
+                logininfor.setLoginLocation(address);
+                logininfor.setBrowser(browser);
+                logininfor.setOs(os);
+                logininfor.setMsg(message);
+                // 日志状态
+                if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
+                {
+                    logininfor.setStatus(Constants.SUCCESS);
+                }
+                else if (Constants.LOGIN_FAIL.equals(status))
+                {
+                    logininfor.setStatus(Constants.FAIL);
+                }
+                // 插入数据
+                SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
+            }
+        };
+    }
+
+    /**
+     * 操作日志记录
+     * 
+     * @param operLog 操作日志信息
+     * @return 任务task
+     */
+    public static TimerTask recordOper(final SysOperLog operLog)
+    {
+        return new TimerTask()
+        {
+            @Override
+            public void run()
+            {
+                // 远程查询操作地点
+                operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
+                SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
+            }
+        };
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/pt-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java
new file mode 100644
index 0000000..6c776ce
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java
@@ -0,0 +1,28 @@
+package com.ruoyi.framework.security.context;
+
+import org.springframework.security.core.Authentication;
+
+/**
+ * 身份验证信息
+ * 
+ * @author ruoyi
+ */
+public class AuthenticationContextHolder
+{
+    private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>();
+
+    public static Authentication getContext()
+    {
+        return contextHolder.get();
+    }
+
+    public static void setContext(Authentication context)
+    {
+        contextHolder.set(context);
+    }
+
+    public static void clearContext()
+    {
+        contextHolder.remove();
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/pt-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java
new file mode 100644
index 0000000..5472f3d
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java
@@ -0,0 +1,27 @@
+package com.ruoyi.framework.security.context;
+
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import com.ruoyi.common.core.text.Convert;
+
+/**
+ * 权限信息
+ * 
+ * @author ruoyi
+ */
+public class PermissionContextHolder
+{
+    private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT";
+
+    public static void setContext(String permission)
+    {
+        RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission,
+                RequestAttributes.SCOPE_REQUEST);
+    }
+
+    public static String getContext()
+    {
+        return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES,
+                RequestAttributes.SCOPE_REQUEST));
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/pt-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java
new file mode 100644
index 0000000..0595a63
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java
@@ -0,0 +1,46 @@
+package com.ruoyi.framework.security.filter;
+
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.web.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * token过滤器 验证token有效性
+ * 
+ * @author ruoyi
+ */
+@Component
+public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
+{
+    @Autowired
+    private TokenService tokenService;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+            throws ServletException, IOException
+    {
+        logger.info("进入sys的token认证");
+        LoginUser loginUser = tokenService.getLoginUser(request);
+        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
+        {
+            tokenService.verifyToken(loginUser);
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
+            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+        }
+        chain.doFilter(request, response);
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/pt-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java
new file mode 100644
index 0000000..93b7032
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java
@@ -0,0 +1,34 @@
+package com.ruoyi.framework.security.handle;
+
+import java.io.IOException;
+import java.io.Serializable;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.constant.HttpStatus;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * 认证失败处理类 返回未授权
+ * 
+ * @author ruoyi
+ */
+@Component
+public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
+{
+    private static final long serialVersionUID = -8970718410437077606L;
+
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
+            throws IOException
+    {
+        int code = HttpStatus.UNAUTHORIZED;
+        String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
+        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/pt-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
new file mode 100644
index 0000000..2f89a91
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
@@ -0,0 +1,53 @@
+package com.ruoyi.framework.security.handle;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.MessageUtils;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.manager.AsyncManager;
+import com.ruoyi.framework.manager.factory.AsyncFactory;
+import com.ruoyi.framework.web.service.TokenService;
+
+/**
+ * 自定义退出处理类 返回成功
+ * 
+ * @author ruoyi
+ */
+@Configuration
+public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
+{
+    @Autowired
+    private TokenService tokenService;
+
+    /**
+     * 退出处理
+     * 
+     * @return
+     */
+    @Override
+    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
+            throws IOException, ServletException
+    {
+        LoginUser loginUser = tokenService.getLoginUser(request);
+        if (StringUtils.isNotNull(loginUser))
+        {
+            String userName = loginUser.getUsername();
+            // 删除用户缓存记录
+            tokenService.delLoginUser(loginUser.getToken());
+            // 记录用户退出日志
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success")));
+        }
+        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success"))));
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java
new file mode 100644
index 0000000..63b03da
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java
@@ -0,0 +1,240 @@
+package com.ruoyi.framework.web.domain;
+
+import java.net.UnknownHostException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import com.ruoyi.common.utils.Arith;
+import com.ruoyi.common.utils.ip.IpUtils;
+import com.ruoyi.framework.web.domain.server.Cpu;
+import com.ruoyi.framework.web.domain.server.Jvm;
+import com.ruoyi.framework.web.domain.server.Mem;
+import com.ruoyi.framework.web.domain.server.Sys;
+import com.ruoyi.framework.web.domain.server.SysFile;
+import oshi.SystemInfo;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.CentralProcessor.TickType;
+import oshi.hardware.GlobalMemory;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.software.os.FileSystem;
+import oshi.software.os.OSFileStore;
+import oshi.software.os.OperatingSystem;
+import oshi.util.Util;
+
+/**
+ * 服务器相关信息
+ * 
+ * @author ruoyi
+ */
+public class Server
+{
+    private static final int OSHI_WAIT_SECOND = 1000;
+    
+    /**
+     * CPU相关信息
+     */
+    private Cpu cpu = new Cpu();
+
+    /**
+     * 內存相关信息
+     */
+    private Mem mem = new Mem();
+
+    /**
+     * JVM相关信息
+     */
+    private Jvm jvm = new Jvm();
+
+    /**
+     * 服务器相关信息
+     */
+    private Sys sys = new Sys();
+
+    /**
+     * 磁盘相关信息
+     */
+    private List<SysFile> sysFiles = new LinkedList<SysFile>();
+
+    public Cpu getCpu()
+    {
+        return cpu;
+    }
+
+    public void setCpu(Cpu cpu)
+    {
+        this.cpu = cpu;
+    }
+
+    public Mem getMem()
+    {
+        return mem;
+    }
+
+    public void setMem(Mem mem)
+    {
+        this.mem = mem;
+    }
+
+    public Jvm getJvm()
+    {
+        return jvm;
+    }
+
+    public void setJvm(Jvm jvm)
+    {
+        this.jvm = jvm;
+    }
+
+    public Sys getSys()
+    {
+        return sys;
+    }
+
+    public void setSys(Sys sys)
+    {
+        this.sys = sys;
+    }
+
+    public List<SysFile> getSysFiles()
+    {
+        return sysFiles;
+    }
+
+    public void setSysFiles(List<SysFile> sysFiles)
+    {
+        this.sysFiles = sysFiles;
+    }
+
+    public void copyTo() throws Exception
+    {
+        SystemInfo si = new SystemInfo();
+        HardwareAbstractionLayer hal = si.getHardware();
+
+        setCpuInfo(hal.getProcessor());
+
+        setMemInfo(hal.getMemory());
+
+        setSysInfo();
+
+        setJvmInfo();
+
+        setSysFiles(si.getOperatingSystem());
+    }
+
+    /**
+     * 设置CPU信息
+     */
+    private void setCpuInfo(CentralProcessor processor)
+    {
+        // CPU信息
+        long[] prevTicks = processor.getSystemCpuLoadTicks();
+        Util.sleep(OSHI_WAIT_SECOND);
+        long[] ticks = processor.getSystemCpuLoadTicks();
+        long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
+        long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
+        long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
+        long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
+        long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
+        long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
+        long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
+        long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
+        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
+        cpu.setCpuNum(processor.getLogicalProcessorCount());
+        cpu.setTotal(totalCpu);
+        cpu.setSys(cSys);
+        cpu.setUsed(user);
+        cpu.setWait(iowait);
+        cpu.setFree(idle);
+    }
+
+    /**
+     * 设置内存信息
+     */
+    private void setMemInfo(GlobalMemory memory)
+    {
+        mem.setTotal(memory.getTotal());
+        mem.setUsed(memory.getTotal() - memory.getAvailable());
+        mem.setFree(memory.getAvailable());
+    }
+
+    /**
+     * 设置服务器信息
+     */
+    private void setSysInfo()
+    {
+        Properties props = System.getProperties();
+        sys.setComputerName(IpUtils.getHostName());
+        sys.setComputerIp(IpUtils.getHostIp());
+        sys.setOsName(props.getProperty("os.name"));
+        sys.setOsArch(props.getProperty("os.arch"));
+        sys.setUserDir(props.getProperty("user.dir"));
+    }
+
+    /**
+     * 设置Java虚拟机
+     */
+    private void setJvmInfo() throws UnknownHostException
+    {
+        Properties props = System.getProperties();
+        jvm.setTotal(Runtime.getRuntime().totalMemory());
+        jvm.setMax(Runtime.getRuntime().maxMemory());
+        jvm.setFree(Runtime.getRuntime().freeMemory());
+        jvm.setVersion(props.getProperty("java.version"));
+        jvm.setHome(props.getProperty("java.home"));
+    }
+
+    /**
+     * 设置磁盘信息
+     */
+    private void setSysFiles(OperatingSystem os)
+    {
+        FileSystem fileSystem = os.getFileSystem();
+        List<OSFileStore> fsArray = fileSystem.getFileStores();
+        for (OSFileStore fs : fsArray)
+        {
+            long free = fs.getUsableSpace();
+            long total = fs.getTotalSpace();
+            long used = total - free;
+            SysFile sysFile = new SysFile();
+            sysFile.setDirName(fs.getMount());
+            sysFile.setSysTypeName(fs.getType());
+            sysFile.setTypeName(fs.getName());
+            sysFile.setTotal(convertFileSize(total));
+            sysFile.setFree(convertFileSize(free));
+            sysFile.setUsed(convertFileSize(used));
+            sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100));
+            sysFiles.add(sysFile);
+        }
+    }
+
+    /**
+     * 字节转换
+     * 
+     * @param size 字节大小
+     * @return 转换后值
+     */
+    public String convertFileSize(long size)
+    {
+        long kb = 1024;
+        long mb = kb * 1024;
+        long gb = mb * 1024;
+        if (size >= gb)
+        {
+            return String.format("%.1f GB", (float) size / gb);
+        }
+        else if (size >= mb)
+        {
+            float f = (float) size / mb;
+            return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
+        }
+        else if (size >= kb)
+        {
+            float f = (float) size / kb;
+            return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
+        }
+        else
+        {
+            return String.format("%d B", size);
+        }
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java
new file mode 100644
index 0000000..a13a66c
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java
@@ -0,0 +1,101 @@
+package com.ruoyi.framework.web.domain.server;
+
+import com.ruoyi.common.utils.Arith;
+
+/**
+ * CPU相关信息
+ * 
+ * @author ruoyi
+ */
+public class Cpu
+{
+    /**
+     * 核心数
+     */
+    private int cpuNum;
+
+    /**
+     * CPU总的使用率
+     */
+    private double total;
+
+    /**
+     * CPU系统使用率
+     */
+    private double sys;
+
+    /**
+     * CPU用户使用率
+     */
+    private double used;
+
+    /**
+     * CPU当前等待率
+     */
+    private double wait;
+
+    /**
+     * CPU当前空闲率
+     */
+    private double free;
+
+    public int getCpuNum()
+    {
+        return cpuNum;
+    }
+
+    public void setCpuNum(int cpuNum)
+    {
+        this.cpuNum = cpuNum;
+    }
+
+    public double getTotal()
+    {
+        return Arith.round(Arith.mul(total, 100), 2);
+    }
+
+    public void setTotal(double total)
+    {
+        this.total = total;
+    }
+
+    public double getSys()
+    {
+        return Arith.round(Arith.mul(sys / total, 100), 2);
+    }
+
+    public void setSys(double sys)
+    {
+        this.sys = sys;
+    }
+
+    public double getUsed()
+    {
+        return Arith.round(Arith.mul(used / total, 100), 2);
+    }
+
+    public void setUsed(double used)
+    {
+        this.used = used;
+    }
+
+    public double getWait()
+    {
+        return Arith.round(Arith.mul(wait / total, 100), 2);
+    }
+
+    public void setWait(double wait)
+    {
+        this.wait = wait;
+    }
+
+    public double getFree()
+    {
+        return Arith.round(Arith.mul(free / total, 100), 2);
+    }
+
+    public void setFree(double free)
+    {
+        this.free = free;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java
new file mode 100644
index 0000000..1fdc6ac
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java
@@ -0,0 +1,130 @@
+package com.ruoyi.framework.web.domain.server;
+
+import java.lang.management.ManagementFactory;
+import com.ruoyi.common.utils.Arith;
+import com.ruoyi.common.utils.DateUtils;
+
+/**
+ * JVM相关信息
+ * 
+ * @author ruoyi
+ */
+public class Jvm
+{
+    /**
+     * 当前JVM占用的内存总数(M)
+     */
+    private double total;
+
+    /**
+     * JVM最大可用内存总数(M)
+     */
+    private double max;
+
+    /**
+     * JVM空闲内存(M)
+     */
+    private double free;
+
+    /**
+     * JDK版本
+     */
+    private String version;
+
+    /**
+     * JDK路径
+     */
+    private String home;
+
+    public double getTotal()
+    {
+        return Arith.div(total, (1024 * 1024), 2);
+    }
+
+    public void setTotal(double total)
+    {
+        this.total = total;
+    }
+
+    public double getMax()
+    {
+        return Arith.div(max, (1024 * 1024), 2);
+    }
+
+    public void setMax(double max)
+    {
+        this.max = max;
+    }
+
+    public double getFree()
+    {
+        return Arith.div(free, (1024 * 1024), 2);
+    }
+
+    public void setFree(double free)
+    {
+        this.free = free;
+    }
+
+    public double getUsed()
+    {
+        return Arith.div(total - free, (1024 * 1024), 2);
+    }
+
+    public double getUsage()
+    {
+        return Arith.mul(Arith.div(total - free, total, 4), 100);
+    }
+
+    /**
+     * 获取JDK名称
+     */
+    public String getName()
+    {
+        return ManagementFactory.getRuntimeMXBean().getVmName();
+    }
+
+    public String getVersion()
+    {
+        return version;
+    }
+
+    public void setVersion(String version)
+    {
+        this.version = version;
+    }
+
+    public String getHome()
+    {
+        return home;
+    }
+
+    public void setHome(String home)
+    {
+        this.home = home;
+    }
+
+    /**
+     * JDK启动时间
+     */
+    public String getStartTime()
+    {
+        return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate());
+    }
+
+    /**
+     * JDK运行时间
+     */
+    public String getRunTime()
+    {
+        return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate());
+    }
+
+    /**
+     * 运行参数
+     */
+    public String getInputArgs()
+    {
+        return ManagementFactory.getRuntimeMXBean().getInputArguments().toString();
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java
new file mode 100644
index 0000000..13eec52
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java
@@ -0,0 +1,61 @@
+package com.ruoyi.framework.web.domain.server;
+
+import com.ruoyi.common.utils.Arith;
+
+/**
+ * 內存相关信息
+ * 
+ * @author ruoyi
+ */
+public class Mem
+{
+    /**
+     * 内存总量
+     */
+    private double total;
+
+    /**
+     * 已用内存
+     */
+    private double used;
+
+    /**
+     * 剩余内存
+     */
+    private double free;
+
+    public double getTotal()
+    {
+        return Arith.div(total, (1024 * 1024 * 1024), 2);
+    }
+
+    public void setTotal(long total)
+    {
+        this.total = total;
+    }
+
+    public double getUsed()
+    {
+        return Arith.div(used, (1024 * 1024 * 1024), 2);
+    }
+
+    public void setUsed(long used)
+    {
+        this.used = used;
+    }
+
+    public double getFree()
+    {
+        return Arith.div(free, (1024 * 1024 * 1024), 2);
+    }
+
+    public void setFree(long free)
+    {
+        this.free = free;
+    }
+
+    public double getUsage()
+    {
+        return Arith.mul(Arith.div(used, total, 4), 100);
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java
new file mode 100644
index 0000000..45d64d9
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java
@@ -0,0 +1,84 @@
+package com.ruoyi.framework.web.domain.server;
+
+/**
+ * 系统相关信息
+ * 
+ * @author ruoyi
+ */
+public class Sys
+{
+    /**
+     * 服务器名称
+     */
+    private String computerName;
+
+    /**
+     * 服务器Ip
+     */
+    private String computerIp;
+
+    /**
+     * 项目路径
+     */
+    private String userDir;
+
+    /**
+     * 操作系统
+     */
+    private String osName;
+
+    /**
+     * 系统架构
+     */
+    private String osArch;
+
+    public String getComputerName()
+    {
+        return computerName;
+    }
+
+    public void setComputerName(String computerName)
+    {
+        this.computerName = computerName;
+    }
+
+    public String getComputerIp()
+    {
+        return computerIp;
+    }
+
+    public void setComputerIp(String computerIp)
+    {
+        this.computerIp = computerIp;
+    }
+
+    public String getUserDir()
+    {
+        return userDir;
+    }
+
+    public void setUserDir(String userDir)
+    {
+        this.userDir = userDir;
+    }
+
+    public String getOsName()
+    {
+        return osName;
+    }
+
+    public void setOsName(String osName)
+    {
+        this.osName = osName;
+    }
+
+    public String getOsArch()
+    {
+        return osArch;
+    }
+
+    public void setOsArch(String osArch)
+    {
+        this.osArch = osArch;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java
new file mode 100644
index 0000000..1320cde
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java
@@ -0,0 +1,114 @@
+package com.ruoyi.framework.web.domain.server;
+
+/**
+ * 系统文件相关信息
+ * 
+ * @author ruoyi
+ */
+public class SysFile
+{
+    /**
+     * 盘符路径
+     */
+    private String dirName;
+
+    /**
+     * 盘符类型
+     */
+    private String sysTypeName;
+
+    /**
+     * 文件类型
+     */
+    private String typeName;
+
+    /**
+     * 总大小
+     */
+    private String total;
+
+    /**
+     * 剩余大小
+     */
+    private String free;
+
+    /**
+     * 已经使用量
+     */
+    private String used;
+
+    /**
+     * 资源的使用率
+     */
+    private double usage;
+
+    public String getDirName()
+    {
+        return dirName;
+    }
+
+    public void setDirName(String dirName)
+    {
+        this.dirName = dirName;
+    }
+
+    public String getSysTypeName()
+    {
+        return sysTypeName;
+    }
+
+    public void setSysTypeName(String sysTypeName)
+    {
+        this.sysTypeName = sysTypeName;
+    }
+
+    public String getTypeName()
+    {
+        return typeName;
+    }
+
+    public void setTypeName(String typeName)
+    {
+        this.typeName = typeName;
+    }
+
+    public String getTotal()
+    {
+        return total;
+    }
+
+    public void setTotal(String total)
+    {
+        this.total = total;
+    }
+
+    public String getFree()
+    {
+        return free;
+    }
+
+    public void setFree(String free)
+    {
+        this.free = free;
+    }
+
+    public String getUsed()
+    {
+        return used;
+    }
+
+    public void setUsed(String used)
+    {
+        this.used = used;
+    }
+
+    public double getUsage()
+    {
+        return usage;
+    }
+
+    public void setUsage(double usage)
+    {
+        this.usage = usage;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/pt-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..a8ea1cc
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
@@ -0,0 +1,152 @@
+package com.ruoyi.framework.web.exception;
+
+import javax.servlet.http.HttpServletRequest;
+
+import cn.hutool.core.util.ObjUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.validation.BindException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingPathVariableException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+import com.ruoyi.common.constant.HttpStatus;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.exception.DemoModeException;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.html.EscapeUtil;
+
+/**
+ * 全局异常处理器
+ * 
+ * @author ruoyi
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler
+{
+    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    /**
+     * 权限校验异常
+     */
+    @ExceptionHandler(AccessDeniedException.class)
+    public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
+        return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
+    }
+
+    /**
+     * 请求方式不支持
+     */
+    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+    public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
+            HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
+        return AjaxResult.error(e.getMessage());
+    }
+
+    /**
+     * 业务异常
+     */
+    @ExceptionHandler(ServiceException.class)
+    public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
+    {
+        log.error(e.getMessage());
+        Integer code = e.getCode();
+        return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
+    }
+
+    /**
+     * 请求路径中缺少必需的路径变量
+     */
+    @ExceptionHandler(MissingPathVariableException.class)
+    public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
+        return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
+    }
+
+    /**
+     * 请求参数类型不匹配
+     */
+    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
+    public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        String value = Convert.toStr(e.getValue());
+        if (StringUtils.isNotEmpty(value))
+        {
+            value = EscapeUtil.clean(value);
+        }
+        log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
+        return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value));
+    }
+
+    /**
+     * 拦截未知的运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',发生未知异常.", requestURI, e);
+        if(ObjUtil.isNotEmpty(e.getCause())){
+            return AjaxResult.error(e.getCause().getMessage());
+        }
+        return AjaxResult.error(e.getMessage());
+    }
+
+    /**
+     * 系统异常
+     */
+    @ExceptionHandler(Exception.class)
+    public AjaxResult handleException(Exception e, HttpServletRequest request)
+    {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',发生系统异常.", requestURI, e);
+        return AjaxResult.error(e.getMessage());
+    }
+
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(BindException.class)
+    public AjaxResult handleBindException(BindException e)
+    {
+        log.error(e.getMessage(), e);
+        String message = e.getAllErrors().get(0).getDefaultMessage();
+        return AjaxResult.error(message);
+    }
+
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
+    {
+        log.error(e.getMessage(), e);
+        String message = e.getBindingResult().getFieldError().getDefaultMessage();
+        return AjaxResult.error(message);
+    }
+
+    /**
+     * 演示模式异常
+     */
+    @ExceptionHandler(DemoModeException.class)
+    public AjaxResult handleDemoModeException(DemoModeException e)
+    {
+        return AjaxResult.error("演示模式,不允许操作");
+    }
+
+
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/pt-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
new file mode 100644
index 0000000..5c35484
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
@@ -0,0 +1,137 @@
+package com.ruoyi.framework.web.service;
+
+import java.util.Set;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.security.context.PermissionContextHolder;
+
+/**
+ * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母
+ * 
+ * @author ruoyi
+ */
+@Service("ss")
+public class PermissionService {
+    /**
+     * 验证用户是否具备某权限
+     *
+     * @param permission 权限字符串
+     * @return 用户是否具备某权限
+     */
+    public boolean hasPermi(String permission) {
+        if (StringUtils.isEmpty(permission)) {
+            return false;
+        }
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
+            return false;
+        }
+        PermissionContextHolder.setContext(permission);
+        return hasPermissions(loginUser.getPermissions(), permission);
+    }
+
+    /**
+     * 验证用户是否不具备某权限,与 hasPermi逻辑相反
+     *
+     * @param permission 权限字符串
+     * @return 用户是否不具备某权限
+     */
+    public boolean lacksPermi(String permission) {
+        return hasPermi(permission) != true;
+    }
+
+    /**
+     * 验证用户是否具有以下任意一个权限
+     *
+     * @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表
+     * @return 用户是否具有以下任意一个权限
+     */
+    public boolean hasAnyPermi(String permissions) {
+        if (StringUtils.isEmpty(permissions)) {
+            return false;
+        }
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
+            return false;
+        }
+        PermissionContextHolder.setContext(permissions);
+        Set<String> authorities = loginUser.getPermissions();
+        for (String permission : permissions.split(Constants.PERMISSION_DELIMETER)) {
+            if (permission != null && hasPermissions(authorities, permission)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断用户是否拥有某个角色
+     *
+     * @param role 角色字符串
+     * @return 用户是否具备某角色
+     */
+    public boolean hasRole(String role) {
+        if (StringUtils.isEmpty(role)) {
+            return false;
+        }
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
+            return false;
+        }
+        for (SysRole sysRole : loginUser.getUser().getRoles()) {
+            String roleKey = sysRole.getRoleKey();
+            if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 验证用户是否不具备某角色,与 isRole逻辑相反。
+     *
+     * @param role 角色名称
+     * @return 用户是否不具备某角色
+     */
+    public boolean lacksRole(String role) {
+        return hasRole(role) != true;
+    }
+
+    /**
+     * 验证用户是否具有以下任意一个角色
+     *
+     * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
+     * @return 用户是否具有以下任意一个角色
+     */
+    public boolean hasAnyRoles(String roles) {
+        if (StringUtils.isEmpty(roles)) {
+            return false;
+        }
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
+            return false;
+        }
+        for (String role : roles.split(Constants.ROLE_DELIMETER)) {
+            if (hasRole(role)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断是否包含权限
+     *
+     * @param permissions 权限列表
+     * @param permission  权限字符串
+     * @return 用户是否具备某权限
+     */
+    private boolean hasPermissions(Set<String> permissions, String permission) {
+        return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
new file mode 100644
index 0000000..72eb781
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -0,0 +1,183 @@
+package com.ruoyi.framework.web.service;
+
+import javax.annotation.Resource;
+
+import com.ruoyi.common.constant.SecurityConstants;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.system.object.dto.SetPasswordDTO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.constant.CacheConstants;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.exception.user.BlackListException;
+import com.ruoyi.common.exception.user.CaptchaException;
+import com.ruoyi.common.exception.user.CaptchaExpireException;
+import com.ruoyi.common.exception.user.UserNotExistsException;
+import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.MessageUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.ip.IpUtils;
+import com.ruoyi.framework.manager.AsyncManager;
+import com.ruoyi.framework.manager.factory.AsyncFactory;
+import com.ruoyi.framework.security.context.AuthenticationContextHolder;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysUserService;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 登录校验方法
+ * 
+ * @author ruoyi
+ */
+@Component
+public class SysLoginService {
+    @Autowired
+    private TokenService tokenService;
+
+    @Resource
+    private AuthenticationManager authenticationManager;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    /**
+     * 登录验证
+     *
+     * @param username 用户名
+     * @param password 密码
+     * @param code     验证码
+     * @param uuid     唯一标识
+     * @return 结果
+     */
+    public String login(String username, String password, String code, String uuid) {
+        // 验证码校验
+        validateCaptcha(username, code, uuid);
+        // 登录前置校验
+        loginPreCheck(username, password);
+        // 用户验证
+        Authentication authentication = null;
+        try {
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
+            AuthenticationContextHolder.setContext(authenticationToken);
+            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
+            authentication = authenticationManager.authenticate(authenticationToken);
+        } catch (Exception e) {
+            if (e instanceof BadCredentialsException) {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
+                throw new UserPasswordNotMatchException();
+            } else {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
+                throw new ServiceException(e.getMessage());
+            }
+        } finally {
+            AuthenticationContextHolder.clearContext();
+        }
+        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
+        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
+        recordLoginInfo(loginUser.getUserId());
+        // 生成token
+        return tokenService.createToken(loginUser);
+    }
+
+    /**
+     * 校验验证码
+     *
+     * @param username 用户名
+     * @param code     验证码
+     * @param uuid     唯一标识
+     * @return 结果
+     */
+    public void validateCaptcha(String username, String code, String uuid) {
+        boolean captchaEnabled = configService.selectCaptchaEnabled();
+        if (captchaEnabled) {
+            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
+            String captcha = redisCache.getCacheObject(verifyKey);
+            if (captcha == null) {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
+                throw new CaptchaExpireException();
+            }
+            redisCache.deleteObject(verifyKey);
+            if (!code.equalsIgnoreCase(captcha)) {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
+                throw new CaptchaException();
+            }
+        }
+    }
+
+    /**
+     * 登录前置校验
+     *
+     * @param username 用户名
+     * @param password 用户密码
+     */
+    public void loginPreCheck(String username, String password) {
+        // 用户名或密码为空 错误
+        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
+            throw new UserNotExistsException();
+        }
+        // 密码如果不在指定范围内 错误
+        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
+                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
+            throw new UserPasswordNotMatchException();
+        }
+        // 用户名不在指定范围内 错误
+        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
+                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
+            throw new UserPasswordNotMatchException();
+        }
+        // IP黑名单校验
+        String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
+        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
+            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
+            throw new BlackListException();
+        }
+    }
+
+    /**
+     * 记录登录信息
+     *
+     * @param userId 用户ID
+     */
+    public void recordLoginInfo(Long userId) {
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(userId);
+        sysUser.setLoginIp(IpUtils.getIpAddr());
+        sysUser.setLoginDate(DateUtils.getNowDate());
+        userService.updateUserProfile(sysUser);
+    }
+
+    public void setPassword(SetPasswordDTO setPasswordDTO) {
+        //检查密码
+        SysUser sysUser = (SysUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        if (!SecurityUtils.matchesPassword(setPasswordDTO.getPwd(), sysUser.getPassword())) {
+            throw new ServiceException("密码错误");
+        }
+        //修改密码
+        sysUser.setPassword(SecurityUtils.encryptPassword(setPasswordDTO.getNewPwd()));
+        sysUser.setUpdateBy(sysUser.getUserName());
+        sysUser.setUpdateTime(DateUtils.getNowDate());
+        userService.updateUserProfile(sysUser);
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java
new file mode 100644
index 0000000..6728c7b
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java
@@ -0,0 +1,86 @@
+package com.ruoyi.framework.web.service;
+
+import java.util.concurrent.TimeUnit;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.constant.CacheConstants;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
+import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.framework.security.context.AuthenticationContextHolder;
+
+/**
+ * 登录密码方法
+ * 
+ * @author ruoyi
+ */
+@Component
+public class SysPasswordService
+{
+    @Autowired
+    private RedisCache redisCache;
+
+    @Value(value = "${user.password.maxRetryCount}")
+    private int maxRetryCount;
+
+    @Value(value = "${user.password.lockTime}")
+    private int lockTime;
+
+    /**
+     * 登录账户密码错误次数缓存键名
+     * 
+     * @param username 用户名
+     * @return 缓存键key
+     */
+    private String getCacheKey(String username)
+    {
+        return CacheConstants.PWD_ERR_CNT_KEY + username;
+    }
+
+    public void validate(SysUser user)
+    {
+        Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
+        String username = usernamePasswordAuthenticationToken.getName();
+        String password = usernamePasswordAuthenticationToken.getCredentials().toString();
+
+        Integer retryCount = redisCache.getCacheObject(getCacheKey(username));
+
+        if (retryCount == null)
+        {
+            retryCount = 0;
+        }
+
+        if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
+        {
+            throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
+        }
+
+        if (!matches(user, password))
+        {
+            retryCount = retryCount + 1;
+            redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
+            throw new UserPasswordNotMatchException();
+        }
+        else
+        {
+            clearLoginRecordCache(username);
+        }
+    }
+
+    public boolean matches(SysUser user, String rawPassword)
+    {
+        return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
+    }
+
+    public void clearLoginRecordCache(String loginName)
+    {
+        if (redisCache.hasKey(getCacheKey(loginName)))
+        {
+            redisCache.deleteObject(getCacheKey(loginName));
+        }
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java
new file mode 100644
index 0000000..c4d0fa5
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java
@@ -0,0 +1,88 @@
+package com.ruoyi.framework.web.service;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.service.ISysMenuService;
+import com.ruoyi.system.service.ISysRoleService;
+
+/**
+ * 用户权限处理
+ * 
+ * @author ruoyi
+ */
+@Component
+public class SysPermissionService
+{
+    @Autowired
+    private ISysRoleService roleService;
+
+    @Autowired
+    private ISysMenuService menuService;
+
+    /**
+     * 获取角色数据权限
+     * 
+     * @param user 用户信息
+     * @return 角色权限信息
+     */
+    public Set<String> getRolePermission(SysUser user)
+    {
+        Set<String> roles = new HashSet<String>();
+        // 管理员拥有所有权限
+        if (user.isAdmin())
+        {
+            roles.add("admin");
+        }
+        else
+        {
+            roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId()));
+        }
+        return roles;
+    }
+
+    /**
+     * 获取菜单数据权限
+     * 
+     * @param user 用户信息
+     * @return 菜单权限信息
+     */
+    public Set<String> getMenuPermission(SysUser user)
+    {
+        Set<String> perms = new HashSet<String>();
+        // 管理员拥有所有权限
+        if (user.isAdmin())
+        {
+            perms.add("*:*:*");
+        }
+        else
+        {
+            List<SysRole> roles = user.getRoles();
+            if (!CollectionUtils.isEmpty(roles))
+            {
+                // 多角色设置permissions属性,以便数据权限匹配权限
+                for (SysRole role : roles)
+                {
+                    if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin())
+                    {
+                        Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
+                        role.setPermissions(rolePerms);
+                        perms.addAll(rolePerms);
+                    }
+                }
+            }
+            else
+            {
+                perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));
+            }
+        }
+        return perms;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java
new file mode 100644
index 0000000..f2afe31
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java
@@ -0,0 +1,115 @@
+package com.ruoyi.framework.web.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.constant.CacheConstants;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.RegisterBody;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.exception.user.CaptchaException;
+import com.ruoyi.common.exception.user.CaptchaExpireException;
+import com.ruoyi.common.utils.MessageUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.manager.AsyncManager;
+import com.ruoyi.framework.manager.factory.AsyncFactory;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysUserService;
+
+/**
+ * 注册校验方法
+ * 
+ * @author ruoyi
+ */
+@Component
+public class SysRegisterService
+{
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 注册
+     */
+    public String register(RegisterBody registerBody)
+    {
+        String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword();
+        SysUser sysUser = new SysUser();
+        sysUser.setUserName(username);
+
+        // 验证码开关
+        boolean captchaEnabled = configService.selectCaptchaEnabled();
+        if (captchaEnabled)
+        {
+            validateCaptcha(username, registerBody.getCode(), registerBody.getUuid());
+        }
+
+        if (StringUtils.isEmpty(username))
+        {
+            msg = "用户名不能为空";
+        }
+        else if (StringUtils.isEmpty(password))
+        {
+            msg = "用户密码不能为空";
+        }
+        else if (username.length() < UserConstants.USERNAME_MIN_LENGTH
+                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
+        {
+            msg = "账户长度必须在2到20个字符之间";
+        }
+        else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
+                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
+        {
+            msg = "密码长度必须在5到20个字符之间";
+        }
+        else if (!userService.checkUserNameUnique(sysUser))
+        {
+            msg = "保存用户'" + username + "'失败,注册账号已存在";
+        }
+        else
+        {
+            sysUser.setNickName(username);
+            sysUser.setPassword(SecurityUtils.encryptPassword(password));
+            boolean regFlag = userService.registerUser(sysUser);
+            if (!regFlag)
+            {
+                msg = "注册失败,请联系系统管理人员";
+            }
+            else
+            {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success")));
+            }
+        }
+        return msg;
+    }
+
+    /**
+     * 校验验证码
+     * 
+     * @param username 用户名
+     * @param code 验证码
+     * @param uuid 唯一标识
+     * @return 结果
+     */
+    public void validateCaptcha(String username, String code, String uuid)
+    {
+        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
+        String captcha = redisCache.getCacheObject(verifyKey);
+        redisCache.deleteObject(verifyKey);
+        if (captcha == null)
+        {
+            throw new CaptchaExpireException();
+        }
+        if (!code.equalsIgnoreCase(captcha))
+        {
+            throw new CaptchaException();
+        }
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/pt-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
new file mode 100644
index 0000000..1caef69
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
@@ -0,0 +1,233 @@
+package com.ruoyi.framework.web.service;
+
+import com.ruoyi.common.constant.CacheConstants;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.ip.AddressUtils;
+import com.ruoyi.common.utils.ip.IpUtils;
+import com.ruoyi.common.utils.uuid.IdUtils;
+import eu.bitwalker.useragentutils.UserAgent;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * token验证处理
+ * 
+ * @author ruoyi
+ */
+@Component
+public class TokenService
+{
+    private static final Logger log = LoggerFactory.getLogger(TokenService.class);
+
+    // 令牌自定义标识
+    @Value("${token.header}")
+    private String header;
+
+    // 令牌秘钥
+    @Value("${token.secret}")
+    private String secret;
+
+    // 令牌有效期(默认30分钟)
+    @Value("${token.expireTime}")
+    private int expireTime;
+
+    protected static final long MILLIS_SECOND = 1000;
+
+    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
+
+    private static final Long MILLIS_MINUTE_TWENTY = 20 * 60 * 1000L;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 获取用户身份信息
+     * 
+     * @return 用户信息
+     */
+    public LoginUser getLoginUser(HttpServletRequest request)
+    {
+        // 获取请求携带的令牌
+        String token = getToken(request);
+        if (StringUtils.isNotEmpty(token))
+        {
+            try
+            {
+                Claims claims = parseToken(token);
+                // 解析对应的权限以及用户信息
+                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
+                String userKey = getTokenKey(uuid);
+                LoginUser user = redisCache.getCacheObject(userKey);
+                return user;
+            }
+            catch (Exception e)
+            {
+                log.error("获取用户信息异常'{}'", e.getMessage());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 设置用户身份信息
+     */
+    public void setLoginUser(LoginUser loginUser)
+    {
+        if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
+        {
+            refreshToken(loginUser);
+        }
+    }
+
+    /**
+     * 删除用户身份信息
+     */
+    public void delLoginUser(String token)
+    {
+        if (StringUtils.isNotEmpty(token))
+        {
+            String userKey = getTokenKey(token);
+            redisCache.deleteObject(userKey);
+        }
+    }
+
+    /**
+     * 创建令牌
+     * 
+     * @param loginUser 用户信息
+     * @return 令牌
+     */
+    public String createToken(LoginUser loginUser)
+    {
+        String token = IdUtils.fastUUID();
+        loginUser.setToken(token);
+        setUserAgent(loginUser);
+        refreshToken(loginUser);
+
+        Map<String, Object> claims = new HashMap<>();
+        claims.put(Constants.LOGIN_USER_KEY, token);
+        claims.put(Constants.JWT_USERNAME, loginUser.getUsername());
+        return createToken(claims);
+    }
+
+    /**
+     * 验证令牌有效期,相差不足20分钟,自动刷新缓存
+     * 
+     * @param loginUser 登录信息
+     * @return 令牌
+     */
+    public void verifyToken(LoginUser loginUser)
+    {
+        long expireTime = loginUser.getExpireTime();
+        long currentTime = System.currentTimeMillis();
+        if (expireTime - currentTime <= MILLIS_MINUTE_TWENTY)
+        {
+            refreshToken(loginUser);
+        }
+    }
+
+    /**
+     * 刷新令牌有效期
+     * 
+     * @param loginUser 登录信息
+     */
+    public void refreshToken(LoginUser loginUser)
+    {
+        loginUser.setLoginTime(System.currentTimeMillis());
+        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
+        // 根据uuid将loginUser缓存
+        String userKey = getTokenKey(loginUser.getToken());
+        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
+    }
+
+    /**
+     * 设置用户代理信息
+     * 
+     * @param loginUser 登录信息
+     */
+    public void setUserAgent(LoginUser loginUser)
+    {
+        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
+        String ip = IpUtils.getIpAddr();
+        loginUser.setIpaddr(ip);
+        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
+        loginUser.setBrowser(userAgent.getBrowser().getName());
+        loginUser.setOs(userAgent.getOperatingSystem().getName());
+    }
+
+    /**
+     * 从数据声明生成令牌
+     *
+     * @param claims 数据声明
+     * @return 令牌
+     */
+    private String createToken(Map<String, Object> claims)
+    {
+        String token = Jwts.builder()
+                .setClaims(claims)
+                .signWith(SignatureAlgorithm.HS512, secret).compact();
+        return token;
+    }
+
+    /**
+     * 从令牌中获取数据声明
+     *
+     * @param token 令牌
+     * @return 数据声明
+     */
+    private Claims parseToken(String token)
+    {
+        return Jwts.parser()
+                .setSigningKey(secret)
+                .parseClaimsJws(token)
+                .getBody();
+    }
+
+    /**
+     * 从令牌中获取用户名
+     *
+     * @param token 令牌
+     * @return 用户名
+     */
+    public String getUsernameFromToken(String token)
+    {
+        Claims claims = parseToken(token);
+        return claims.getSubject();
+    }
+
+    /**
+     * 获取请求token
+     *
+     * @param request
+     * @return token
+     */
+    private String getToken(HttpServletRequest request)
+    {
+        String token = request.getHeader(header);
+        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
+        {
+            token = token.replace(Constants.TOKEN_PREFIX, "");
+        }
+        return token;
+    }
+
+    private String getTokenKey(String uuid)
+    {
+        return CacheConstants.LOGIN_TOKEN_KEY + uuid;
+    }
+}
diff --git a/pt-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/pt-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
new file mode 100644
index 0000000..5dcdf90
--- /dev/null
+++ b/pt-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
@@ -0,0 +1,66 @@
+package com.ruoyi.framework.web.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.enums.UserStatus;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.MessageUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.service.ISysUserService;
+
+/**
+ * 用户验证处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class UserDetailsServiceImpl implements UserDetailsService
+{
+    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
+
+    @Autowired
+    private ISysUserService userService;
+    
+    @Autowired
+    private SysPasswordService passwordService;
+
+    @Autowired
+    private SysPermissionService permissionService;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
+    {
+        SysUser user = userService.selectUserByUserName(username);
+        if (StringUtils.isNull(user))
+        {
+            log.info("登录用户:{} 不存在.", username);
+            throw new ServiceException(MessageUtils.message("user.not.exists"));
+        }
+        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
+        {
+            log.info("登录用户:{} 已被删除.", username);
+            throw new ServiceException(MessageUtils.message("user.password.delete"));
+        }
+        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
+        {
+            log.info("登录用户:{} 已被停用.", username);
+            throw new ServiceException(MessageUtils.message("user.blocked"));
+        }
+
+        passwordService.validate(user);
+
+        return createLoginUser(user);
+    }
+
+    public UserDetails createLoginUser(SysUser user)
+    {
+        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
+    }
+}
diff --git a/pt-framework/target/maven-archiver/pom.properties b/pt-framework/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..7dcd97b
--- /dev/null
+++ b/pt-framework/target/maven-archiver/pom.properties
@@ -0,0 +1,3 @@
+artifactId=pt-framework
+groupId=com.pt
+version=3.8.9
diff --git a/pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..1b74a6e
--- /dev/null
+++ b/pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,48 @@
+com\ruoyi\framework\config\CaptchaConfig.class
+com\ruoyi\framework\config\ServerConfig.class
+com\ruoyi\framework\web\service\SysLoginService.class
+com\ruoyi\framework\config\ThreadPoolConfig$1.class
+com\ruoyi\framework\manager\factory\AsyncFactory$2.class
+com\ruoyi\framework\config\SecurityConfig.class
+com\ruoyi\framework\security\handle\AuthenticationEntryPointImpl.class
+com\ruoyi\framework\security\context\AuthenticationContextHolder.class
+com\ruoyi\framework\web\service\UserDetailsServiceImpl.class
+com\ruoyi\framework\aspectj\RateLimiterAspect.class
+com\ruoyi\framework\web\service\PermissionService.class
+com\ruoyi\framework\web\domain\server\Cpu.class
+com\ruoyi\framework\web\domain\server\Mem.class
+com\ruoyi\framework\config\KaptchaTextCreator.class
+com\ruoyi\framework\config\ResourcesConfig.class
+com\ruoyi\framework\datasource\DynamicDataSourceContextHolder.class
+com\ruoyi\framework\interceptor\RepeatSubmitInterceptor.class
+com\ruoyi\framework\web\domain\server\Jvm.class
+com\ruoyi\framework\config\properties\DruidProperties.class
+com\ruoyi\framework\aspectj\DataSourceAspect.class
+com\ruoyi\framework\aspectj\LogAspect.class
+com\ruoyi\framework\config\I18nConfig.class
+com\ruoyi\framework\config\DruidConfig$1.class
+com\ruoyi\framework\config\FastJson2JsonRedisSerializer.class
+com\ruoyi\framework\manager\factory\AsyncFactory$1.class
+com\ruoyi\framework\web\domain\server\SysFile.class
+com\ruoyi\framework\manager\AsyncManager.class
+com\ruoyi\framework\manager\factory\AsyncFactory.class
+com\ruoyi\framework\web\service\SysPasswordService.class
+com\ruoyi\framework\web\service\SysRegisterService.class
+com\ruoyi\framework\interceptor\impl\SameUrlDataInterceptor.class
+com\ruoyi\framework\security\context\PermissionContextHolder.class
+com\ruoyi\framework\config\FilterConfig.class
+com\ruoyi\framework\security\filter\JwtAuthenticationTokenFilter.class
+com\ruoyi\framework\web\service\TokenService.class
+com\ruoyi\framework\config\ThreadPoolConfig.class
+com\ruoyi\framework\manager\ShutdownManager.class
+com\ruoyi\framework\config\DruidConfig.class
+com\ruoyi\framework\datasource\DynamicDataSource.class
+com\ruoyi\framework\web\domain\server\Sys.class
+com\ruoyi\framework\config\ApplicationConfig.class
+com\ruoyi\framework\config\properties\PermitAllUrlProperties.class
+com\ruoyi\framework\web\service\SysPermissionService.class
+com\ruoyi\framework\config\RedisConfig.class
+com\ruoyi\framework\web\domain\Server.class
+com\ruoyi\framework\security\handle\LogoutSuccessHandlerImpl.class
+com\ruoyi\framework\web\exception\GlobalExceptionHandler.class
+com\ruoyi\framework\aspectj\DataScopeAspect.class
diff --git a/pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..e4445d8
--- /dev/null
+++ b/pt-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,45 @@
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\I18nConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\security\filter\JwtAuthenticationTokenFilter.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\domain\server\Mem.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\KaptchaTextCreator.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\interceptor\impl\SameUrlDataInterceptor.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\FilterConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\RedisConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\service\SysPermissionService.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\MyBatisConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\ResourcesConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\SecurityConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\exception\GlobalExceptionHandler.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\service\TokenService.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\FastJson2JsonRedisSerializer.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\datasource\DynamicDataSource.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\service\SysLoginService.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\ApplicationConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\domain\server\SysFile.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\security\handle\AuthenticationEntryPointImpl.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\service\SysRegisterService.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\ServerConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\interceptor\RepeatSubmitInterceptor.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\manager\factory\AsyncFactory.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\service\UserDetailsServiceImpl.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\domain\server\Jvm.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\aspectj\DataSourceAspect.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\DruidConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\manager\AsyncManager.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\domain\Server.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\service\SysPasswordService.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\manager\ShutdownManager.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\aspectj\LogAspect.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\aspectj\DataScopeAspect.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\datasource\DynamicDataSourceContextHolder.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\properties\PermitAllUrlProperties.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\security\context\PermissionContextHolder.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\ThreadPoolConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\aspectj\RateLimiterAspect.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\CaptchaConfig.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\config\properties\DruidProperties.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\service\PermissionService.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\security\context\AuthenticationContextHolder.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\security\handle\LogoutSuccessHandlerImpl.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\domain\server\Cpu.java
+E:\hlg资料\项目\paotui\pt-framework\src\main\java\com\ruoyi\framework\web\domain\server\Sys.java
diff --git a/pt-system/pom.xml b/pt-system/pom.xml
new file mode 100644
index 0000000..bc06b78
--- /dev/null
+++ b/pt-system/pom.xml
@@ -0,0 +1,28 @@
+<?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-system</artifactId>
+
+    <description>
+        system系统模块
+    </description>
+
+    <dependencies>
+
+        <!-- 通用工具-->
+        <dependency>
+            <groupId>com.pt</groupId>
+            <artifactId>pt-common</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysCache.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysCache.java
new file mode 100644
index 0000000..83f0703
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysCache.java
@@ -0,0 +1,81 @@
+package com.ruoyi.system.domain;
+
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * 缓存信息
+ * 
+ * @author ruoyi
+ */
+public class SysCache
+{
+    /** 缓存名称 */
+    private String cacheName = "";
+
+    /** 缓存键名 */
+    private String cacheKey = "";
+
+    /** 缓存内容 */
+    private String cacheValue = "";
+
+    /** 备注 */
+    private String remark = "";
+
+    public SysCache()
+    {
+
+    }
+
+    public SysCache(String cacheName, String remark)
+    {
+        this.cacheName = cacheName;
+        this.remark = remark;
+    }
+
+    public SysCache(String cacheName, String cacheKey, String cacheValue)
+    {
+        this.cacheName = StringUtils.replace(cacheName, ":", "");
+        this.cacheKey = StringUtils.replace(cacheKey, cacheName, "");
+        this.cacheValue = cacheValue;
+    }
+
+    public String getCacheName()
+    {
+        return cacheName;
+    }
+
+    public void setCacheName(String cacheName)
+    {
+        this.cacheName = cacheName;
+    }
+
+    public String getCacheKey()
+    {
+        return cacheKey;
+    }
+
+    public void setCacheKey(String cacheKey)
+    {
+        this.cacheKey = cacheKey;
+    }
+
+    public String getCacheValue()
+    {
+        return cacheValue;
+    }
+
+    public void setCacheValue(String cacheValue)
+    {
+        this.cacheValue = cacheValue;
+    }
+
+    public String getRemark()
+    {
+        return remark;
+    }
+
+    public void setRemark(String remark)
+    {
+        this.remark = remark;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysConfig.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysConfig.java
new file mode 100644
index 0000000..c54678c
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysConfig.java
@@ -0,0 +1,111 @@
+package com.ruoyi.system.domain;
+
+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.core.domain.BaseEntity;
+
+/**
+ * 参数配置表 sys_config
+ * 
+ * @author ruoyi
+ */
+public class SysConfig extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 参数主键 */
+    @Excel(name = "参数主键", cellType = ColumnType.NUMERIC)
+    private Long configId;
+
+    /** 参数名称 */
+    @Excel(name = "参数名称")
+    private String configName;
+
+    /** 参数键名 */
+    @Excel(name = "参数键名")
+    private String configKey;
+
+    /** 参数键值 */
+    @Excel(name = "参数键值")
+    private String configValue;
+
+    /** 系统内置(Y是 N否) */
+    @Excel(name = "系统内置", readConverterExp = "Y=是,N=否")
+    private String configType;
+
+    public Long getConfigId()
+    {
+        return configId;
+    }
+
+    public void setConfigId(Long configId)
+    {
+        this.configId = configId;
+    }
+
+    @NotBlank(message = "参数名称不能为空")
+    @Size(min = 0, max = 100, message = "参数名称不能超过100个字符")
+    public String getConfigName()
+    {
+        return configName;
+    }
+
+    public void setConfigName(String configName)
+    {
+        this.configName = configName;
+    }
+
+    @NotBlank(message = "参数键名长度不能为空")
+    @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符")
+    public String getConfigKey()
+    {
+        return configKey;
+    }
+
+    public void setConfigKey(String configKey)
+    {
+        this.configKey = configKey;
+    }
+
+    @NotBlank(message = "参数键值不能为空")
+    @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符")
+    public String getConfigValue()
+    {
+        return configValue;
+    }
+
+    public void setConfigValue(String configValue)
+    {
+        this.configValue = configValue;
+    }
+
+    public String getConfigType()
+    {
+        return configType;
+    }
+
+    public void setConfigType(String configType)
+    {
+        this.configType = configType;
+    }
+    
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("configId", getConfigId())
+            .append("configName", getConfigName())
+            .append("configKey", getConfigKey())
+            .append("configValue", getConfigValue())
+            .append("configType", getConfigType())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java
new file mode 100644
index 0000000..7fdea30
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java
@@ -0,0 +1,144 @@
+package com.ruoyi.system.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 系统访问记录表 sys_logininfor
+ * 
+ * @author ruoyi
+ */
+public class SysLogininfor extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** ID */
+    @Excel(name = "序号", cellType = ColumnType.NUMERIC)
+    private Long infoId;
+
+    /** 用户账号 */
+    @Excel(name = "用户账号")
+    private String userName;
+
+    /** 登录状态 0成功 1失败 */
+    @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败")
+    private String status;
+
+    /** 登录IP地址 */
+    @Excel(name = "登录地址")
+    private String ipaddr;
+
+    /** 登录地点 */
+    @Excel(name = "登录地点")
+    private String loginLocation;
+
+    /** 浏览器类型 */
+    @Excel(name = "浏览器")
+    private String browser;
+
+    /** 操作系统 */
+    @Excel(name = "操作系统")
+    private String os;
+
+    /** 提示消息 */
+    @Excel(name = "提示消息")
+    private String msg;
+
+    /** 访问时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date loginTime;
+
+    public Long getInfoId()
+    {
+        return infoId;
+    }
+
+    public void setInfoId(Long infoId)
+    {
+        this.infoId = infoId;
+    }
+
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    public void setUserName(String userName)
+    {
+        this.userName = userName;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    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 String getMsg()
+    {
+        return msg;
+    }
+
+    public void setMsg(String msg)
+    {
+        this.msg = msg;
+    }
+
+    public Date getLoginTime()
+    {
+        return loginTime;
+    }
+
+    public void setLoginTime(Date loginTime)
+    {
+        this.loginTime = loginTime;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysNotice.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysNotice.java
new file mode 100644
index 0000000..d95ae52
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysNotice.java
@@ -0,0 +1,125 @@
+package com.ruoyi.system.domain;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.domain.BaseEntity;
+import com.ruoyi.common.xss.Xss;
+
+import java.math.BigDecimal;
+
+/**
+ * 通知公告表 sys_notice
+ * 
+ * @author ruoyi
+ */
+@TableName(value = "sys_notice")
+public class SysNotice extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    @TableId(type = IdType.AUTO)
+    /** 公告ID */
+    private Long noticeId;
+
+    /** 公告标题 */
+    @ApiModelProperty(value = "公告标题")
+    private String noticeTitle;
+
+    /** 公告类型(1通知 2公告) */
+    @ApiModelProperty(value = "类型")
+    private String noticeType;
+
+    /** 公告内容 */
+    private String noticeContent;
+
+    /** 公告状态(0正常 1关闭) */
+    @ApiModelProperty(value = "公告状态")
+    private String status;
+
+    @ApiModelProperty(value = "数量")
+    private BigDecimal countNum;
+
+    public BigDecimal getCountNum() {
+        return countNum;
+    }
+
+    public void setCountNum(BigDecimal countNum) {
+        this.countNum = countNum;
+    }
+
+    public Long getNoticeId()
+    {
+        return noticeId;
+    }
+
+    public void setNoticeId(Long noticeId)
+    {
+        this.noticeId = noticeId;
+    }
+
+    public void setNoticeTitle(String noticeTitle)
+    {
+        this.noticeTitle = noticeTitle;
+    }
+
+    @Xss(message = "公告标题不能包含脚本字符")
+    @NotBlank(message = "公告标题不能为空")
+    @Size(min = 0, max = 50, message = "公告标题不能超过50个字符")
+    public String getNoticeTitle()
+    {
+        return noticeTitle;
+    }
+
+    public void setNoticeType(String noticeType)
+    {
+        this.noticeType = noticeType;
+    }
+
+    public String getNoticeType()
+    {
+        return noticeType;
+    }
+
+    public void setNoticeContent(String noticeContent)
+    {
+        this.noticeContent = noticeContent;
+    }
+
+    public String getNoticeContent()
+    {
+        return noticeContent;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("noticeId", getNoticeId())
+            .append("noticeTitle", getNoticeTitle())
+            .append("noticeType", getNoticeType())
+            .append("noticeContent", getNoticeContent())
+            .append("status", getStatus())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java
new file mode 100644
index 0000000..f6761df
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java
@@ -0,0 +1,269 @@
+package com.ruoyi.system.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 操作日志记录表 oper_log
+ * 
+ * @author ruoyi
+ */
+public class SysOperLog extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 日志主键 */
+    @Excel(name = "操作序号", cellType = ColumnType.NUMERIC)
+    private Long operId;
+
+    /** 操作模块 */
+    @Excel(name = "操作模块")
+    private String title;
+
+    /** 业务类型(0其它 1新增 2修改 3删除) */
+    @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
+    private Integer businessType;
+
+    /** 业务类型数组 */
+    private Integer[] businessTypes;
+
+    /** 请求方法 */
+    @Excel(name = "请求方法")
+    private String method;
+
+    /** 请求方式 */
+    @Excel(name = "请求方式")
+    private String requestMethod;
+
+    /** 操作类别(0其它 1后台用户 2手机端用户) */
+    @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
+    private Integer operatorType;
+
+    /** 操作人员 */
+    @Excel(name = "操作人员")
+    private String operName;
+
+    /** 部门名称 */
+    @Excel(name = "部门名称")
+    private String deptName;
+
+    /** 请求url */
+    @Excel(name = "请求地址")
+    private String operUrl;
+
+    /** 操作地址 */
+    @Excel(name = "操作地址")
+    private String operIp;
+
+    /** 操作地点 */
+    @Excel(name = "操作地点")
+    private String operLocation;
+
+    /** 请求参数 */
+    @Excel(name = "请求参数")
+    private String operParam;
+
+    /** 返回参数 */
+    @Excel(name = "返回参数")
+    private String jsonResult;
+
+    /** 操作状态(0正常 1异常) */
+    @Excel(name = "状态", readConverterExp = "0=正常,1=异常")
+    private Integer status;
+
+    /** 错误消息 */
+    @Excel(name = "错误消息")
+    private String errorMsg;
+
+    /** 操作时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date operTime;
+
+    /** 消耗时间 */
+    @Excel(name = "消耗时间", suffix = "毫秒")
+    private Long costTime;
+
+    public Long getOperId()
+    {
+        return operId;
+    }
+
+    public void setOperId(Long operId)
+    {
+        this.operId = operId;
+    }
+
+    public String getTitle()
+    {
+        return title;
+    }
+
+    public void setTitle(String title)
+    {
+        this.title = title;
+    }
+
+    public Integer getBusinessType()
+    {
+        return businessType;
+    }
+
+    public void setBusinessType(Integer businessType)
+    {
+        this.businessType = businessType;
+    }
+
+    public Integer[] getBusinessTypes()
+    {
+        return businessTypes;
+    }
+
+    public void setBusinessTypes(Integer[] businessTypes)
+    {
+        this.businessTypes = businessTypes;
+    }
+
+    public String getMethod()
+    {
+        return method;
+    }
+
+    public void setMethod(String method)
+    {
+        this.method = method;
+    }
+
+    public String getRequestMethod()
+    {
+        return requestMethod;
+    }
+
+    public void setRequestMethod(String requestMethod)
+    {
+        this.requestMethod = requestMethod;
+    }
+
+    public Integer getOperatorType()
+    {
+        return operatorType;
+    }
+
+    public void setOperatorType(Integer operatorType)
+    {
+        this.operatorType = operatorType;
+    }
+
+    public String getOperName()
+    {
+        return operName;
+    }
+
+    public void setOperName(String operName)
+    {
+        this.operName = operName;
+    }
+
+    public String getDeptName()
+    {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName)
+    {
+        this.deptName = deptName;
+    }
+
+    public String getOperUrl()
+    {
+        return operUrl;
+    }
+
+    public void setOperUrl(String operUrl)
+    {
+        this.operUrl = operUrl;
+    }
+
+    public String getOperIp()
+    {
+        return operIp;
+    }
+
+    public void setOperIp(String operIp)
+    {
+        this.operIp = operIp;
+    }
+
+    public String getOperLocation()
+    {
+        return operLocation;
+    }
+
+    public void setOperLocation(String operLocation)
+    {
+        this.operLocation = operLocation;
+    }
+
+    public String getOperParam()
+    {
+        return operParam;
+    }
+
+    public void setOperParam(String operParam)
+    {
+        this.operParam = operParam;
+    }
+
+    public String getJsonResult()
+    {
+        return jsonResult;
+    }
+
+    public void setJsonResult(String jsonResult)
+    {
+        this.jsonResult = jsonResult;
+    }
+
+    public Integer getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(Integer status)
+    {
+        this.status = status;
+    }
+
+    public String getErrorMsg()
+    {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg)
+    {
+        this.errorMsg = errorMsg;
+    }
+
+    public Date getOperTime()
+    {
+        return operTime;
+    }
+
+    public void setOperTime(Date operTime)
+    {
+        this.operTime = operTime;
+    }
+
+    public Long getCostTime()
+    {
+        return costTime;
+    }
+
+    public void setCostTime(Long costTime)
+    {
+        this.costTime = costTime;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysPost.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysPost.java
new file mode 100644
index 0000000..820a13b
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysPost.java
@@ -0,0 +1,124 @@
+package com.ruoyi.system.domain;
+
+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_post
+ * 
+ * @author ruoyi
+ */
+public class SysPost extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 岗位序号 */
+    @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC)
+    private Long postId;
+
+    /** 岗位编码 */
+    @Excel(name = "岗位编码")
+    private String postCode;
+
+    /** 岗位名称 */
+    @Excel(name = "岗位名称")
+    private String postName;
+
+    /** 岗位排序 */
+    @Excel(name = "岗位排序")
+    private Integer postSort;
+
+    /** 状态(0正常 1停用) */
+    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /** 用户是否存在此岗位标识 默认不存在 */
+    private boolean flag = false;
+
+    public Long getPostId()
+    {
+        return postId;
+    }
+
+    public void setPostId(Long postId)
+    {
+        this.postId = postId;
+    }
+
+    @NotBlank(message = "岗位编码不能为空")
+    @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符")
+    public String getPostCode()
+    {
+        return postCode;
+    }
+
+    public void setPostCode(String postCode)
+    {
+        this.postCode = postCode;
+    }
+
+    @NotBlank(message = "岗位名称不能为空")
+    @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符")
+    public String getPostName()
+    {
+        return postName;
+    }
+
+    public void setPostName(String postName)
+    {
+        this.postName = postName;
+    }
+
+    @NotNull(message = "显示顺序不能为空")
+    public Integer getPostSort()
+    {
+        return postSort;
+    }
+
+    public void setPostSort(Integer postSort)
+    {
+        this.postSort = postSort;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public boolean isFlag()
+    {
+        return flag;
+    }
+
+    public void setFlag(boolean flag)
+    {
+        this.flag = flag;
+    }
+    
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("postId", getPostId())
+            .append("postCode", getPostCode())
+            .append("postName", getPostName())
+            .append("postSort", getPostSort())
+            .append("status", getStatus())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .append("remark", getRemark())
+            .toString();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java
new file mode 100644
index 0000000..47b21bf
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java
@@ -0,0 +1,46 @@
+package com.ruoyi.system.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 角色和部门关联 sys_role_dept
+ * 
+ * @author ruoyi
+ */
+public class SysRoleDept
+{
+    /** 角色ID */
+    private Long roleId;
+    
+    /** 部门ID */
+    private Long deptId;
+
+    public Long getRoleId()
+    {
+        return roleId;
+    }
+
+    public void setRoleId(Long roleId)
+    {
+        this.roleId = roleId;
+    }
+
+    public Long getDeptId()
+    {
+        return deptId;
+    }
+
+    public void setDeptId(Long deptId)
+    {
+        this.deptId = deptId;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("roleId", getRoleId())
+            .append("deptId", getDeptId())
+            .toString();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java
new file mode 100644
index 0000000..de10a74
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java
@@ -0,0 +1,46 @@
+package com.ruoyi.system.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 角色和菜单关联 sys_role_menu
+ * 
+ * @author ruoyi
+ */
+public class SysRoleMenu
+{
+    /** 角色ID */
+    private Long roleId;
+    
+    /** 菜单ID */
+    private Long menuId;
+
+    public Long getRoleId()
+    {
+        return roleId;
+    }
+
+    public void setRoleId(Long roleId)
+    {
+        this.roleId = roleId;
+    }
+
+    public Long getMenuId()
+    {
+        return menuId;
+    }
+
+    public void setMenuId(Long menuId)
+    {
+        this.menuId = menuId;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("roleId", getRoleId())
+            .append("menuId", getMenuId())
+            .toString();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java
new file mode 100644
index 0000000..2bbd318
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java
@@ -0,0 +1,113 @@
+package com.ruoyi.system.domain;
+
+/**
+ * 当前在线会话
+ * 
+ * @author ruoyi
+ */
+public class SysUserOnline
+{
+    /** 会话编号 */
+    private String tokenId;
+
+    /** 部门名称 */
+    private String deptName;
+
+    /** 用户名称 */
+    private String userName;
+
+    /** 登录IP地址 */
+    private String ipaddr;
+
+    /** 登录地址 */
+    private String loginLocation;
+
+    /** 浏览器类型 */
+    private String browser;
+
+    /** 操作系统 */
+    private String os;
+
+    /** 登录时间 */
+    private Long loginTime;
+
+    public String getTokenId()
+    {
+        return tokenId;
+    }
+
+    public void setTokenId(String tokenId)
+    {
+        this.tokenId = tokenId;
+    }
+
+    public String getDeptName()
+    {
+        return deptName;
+    }
+
+    public void setDeptName(String deptName)
+    {
+        this.deptName = deptName;
+    }
+
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    public void setUserName(String userName)
+    {
+        this.userName = userName;
+    }
+
+    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 getLoginTime()
+    {
+        return loginTime;
+    }
+
+    public void setLoginTime(Long loginTime)
+    {
+        this.loginTime = loginTime;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java
new file mode 100644
index 0000000..6e8c416
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java
@@ -0,0 +1,46 @@
+package com.ruoyi.system.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 用户和岗位关联 sys_user_post
+ * 
+ * @author ruoyi
+ */
+public class SysUserPost
+{
+    /** 用户ID */
+    private Long userId;
+    
+    /** 岗位ID */
+    private Long postId;
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public Long getPostId()
+    {
+        return postId;
+    }
+
+    public void setPostId(Long postId)
+    {
+        this.postId = postId;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("userId", getUserId())
+            .append("postId", getPostId())
+            .toString();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java b/pt-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java
new file mode 100644
index 0000000..4d15810
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java
@@ -0,0 +1,46 @@
+package com.ruoyi.system.domain;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 用户和角色关联 sys_user_role
+ * 
+ * @author ruoyi
+ */
+public class SysUserRole
+{
+    /** 用户ID */
+    private Long userId;
+    
+    /** 角色ID */
+    private Long roleId;
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    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("roleId", getRoleId())
+            .toString();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java b/pt-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java
new file mode 100644
index 0000000..a5d5fdc
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java
@@ -0,0 +1,106 @@
+package com.ruoyi.system.domain.vo;
+
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * 路由显示信息
+ * 
+ * @author ruoyi
+ */
+public class MetaVo
+{
+    /**
+     * 设置该路由在侧边栏和面包屑中展示的名字
+     */
+    private String title;
+
+    /**
+     * 设置该路由的图标,对应路径src/assets/icons/svg
+     */
+    private String icon;
+
+    /**
+     * 设置为true,则不会被 <keep-alive>缓存
+     */
+    private boolean noCache;
+
+    /**
+     * 内链地址(http(s)://开头)
+     */
+    private String link;
+
+    public MetaVo()
+    {
+    }
+
+    public MetaVo(String title, String icon)
+    {
+        this.title = title;
+        this.icon = icon;
+    }
+
+    public MetaVo(String title, String icon, boolean noCache)
+    {
+        this.title = title;
+        this.icon = icon;
+        this.noCache = noCache;
+    }
+
+    public MetaVo(String title, String icon, String link)
+    {
+        this.title = title;
+        this.icon = icon;
+        this.link = link;
+    }
+
+    public MetaVo(String title, String icon, boolean noCache, String link)
+    {
+        this.title = title;
+        this.icon = icon;
+        this.noCache = noCache;
+        if (StringUtils.ishttp(link))
+        {
+            this.link = link;
+        }
+    }
+
+    public boolean isNoCache()
+    {
+        return noCache;
+    }
+
+    public void setNoCache(boolean noCache)
+    {
+        this.noCache = noCache;
+    }
+
+    public String getTitle()
+    {
+        return title;
+    }
+
+    public void setTitle(String title)
+    {
+        this.title = title;
+    }
+
+    public String getIcon()
+    {
+        return icon;
+    }
+
+    public void setIcon(String icon)
+    {
+        this.icon = icon;
+    }
+
+    public String getLink()
+    {
+        return link;
+    }
+
+    public void setLink(String link)
+    {
+        this.link = link;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java b/pt-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java
new file mode 100644
index 0000000..afff8c9
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java
@@ -0,0 +1,148 @@
+package com.ruoyi.system.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.List;
+
+/**
+ * 路由配置信息
+ * 
+ * @author ruoyi
+ */
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class RouterVo
+{
+    /**
+     * 路由名字
+     */
+    private String name;
+
+    /**
+     * 路由地址
+     */
+    private String path;
+
+    /**
+     * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现
+     */
+    private boolean hidden;
+
+    /**
+     * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
+     */
+    private String redirect;
+
+    /**
+     * 组件地址
+     */
+    private String component;
+
+    /**
+     * 路由参数:如 {"id": 1, "name": "ry"}
+     */
+    private String query;
+
+    /**
+     * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
+     */
+    private Boolean alwaysShow;
+
+    /**
+     * 其他元素
+     */
+    private MetaVo meta;
+
+    /**
+     * 子路由
+     */
+    private List<RouterVo> children;
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getPath()
+    {
+        return path;
+    }
+
+    public void setPath(String path)
+    {
+        this.path = path;
+    }
+
+    public boolean getHidden()
+    {
+        return hidden;
+    }
+
+    public void setHidden(boolean hidden)
+    {
+        this.hidden = hidden;
+    }
+
+    public String getRedirect()
+    {
+        return redirect;
+    }
+
+    public void setRedirect(String redirect)
+    {
+        this.redirect = redirect;
+    }
+
+    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 Boolean getAlwaysShow()
+    {
+        return alwaysShow;
+    }
+
+    public void setAlwaysShow(Boolean alwaysShow)
+    {
+        this.alwaysShow = alwaysShow;
+    }
+
+    public MetaVo getMeta()
+    {
+        return meta;
+    }
+
+    public void setMeta(MetaVo meta)
+    {
+        this.meta = meta;
+    }
+
+    public List<RouterVo> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<RouterVo> children)
+    {
+        this.children = children;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java
new file mode 100644
index 0000000..13d49d6
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java
@@ -0,0 +1,76 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysConfig;
+
+/**
+ * 参数配置 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysConfigMapper
+{
+    /**
+     * 查询参数配置信息
+     * 
+     * @param config 参数配置信息
+     * @return 参数配置信息
+     */
+    public SysConfig selectConfig(SysConfig config);
+
+    /**
+     * 通过ID查询配置
+     * 
+     * @param configId 参数ID
+     * @return 参数配置信息
+     */
+    public SysConfig selectConfigById(Long configId);
+
+    /**
+     * 查询参数配置列表
+     * 
+     * @param config 参数配置信息
+     * @return 参数配置集合
+     */
+    public List<SysConfig> selectConfigList(SysConfig config);
+
+    /**
+     * 根据键名查询参数配置信息
+     * 
+     * @param configKey 参数键名
+     * @return 参数配置信息
+     */
+    public SysConfig checkConfigKeyUnique(String configKey);
+
+    /**
+     * 新增参数配置
+     * 
+     * @param config 参数配置信息
+     * @return 结果
+     */
+    public int insertConfig(SysConfig config);
+
+    /**
+     * 修改参数配置
+     * 
+     * @param config 参数配置信息
+     * @return 结果
+     */
+    public int updateConfig(SysConfig config);
+
+    /**
+     * 删除参数配置
+     * 
+     * @param configId 参数ID
+     * @return 结果
+     */
+    public int deleteConfigById(Long configId);
+
+    /**
+     * 批量删除参数信息
+     * 
+     * @param configIds 需要删除的参数ID
+     * @return 结果
+     */
+    public int deleteConfigByIds(Long[] configIds);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
new file mode 100644
index 0000000..a6efd73
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
@@ -0,0 +1,129 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.system.object.vo.SysDeptPageVO;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.common.core.domain.entity.SysDept;
+
+/**
+ * 部门管理 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysDeptMapper
+{
+    /**
+     * 查询部门管理数据
+     * 
+     * @param dept 部门信息
+     * @return 部门信息集合
+     */
+    public List<SysDept> selectDeptList(SysDept dept);
+
+    /**
+     * 根据角色ID查询部门树信息
+     * 
+     * @param roleId 角色ID
+     * @param deptCheckStrictly 部门树选择项是否关联显示
+     * @return 选中部门列表
+     */
+    public List<Long> selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly);
+
+    /**
+     * 根据部门ID查询信息
+     * 
+     * @param deptId 部门ID
+     * @return 部门信息
+     */
+    public SysDept selectDeptById(Long deptId);
+
+    /**
+     * 根据ID查询所有子部门
+     * 
+     * @param deptId 部门ID
+     * @return 部门列表
+     */
+    public List<SysDept> selectChildrenDeptById(Long deptId);
+
+    /**
+     * 根据ID查询所有子部门(正常状态)
+     * 
+     * @param deptId 部门ID
+     * @return 子部门数
+     */
+    public int selectNormalChildrenDeptById(Long deptId);
+
+    /**
+     * 是否存在子节点
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    public int hasChildByDeptId(Long deptId);
+
+    /**
+     * 查询部门是否存在用户
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    public int checkDeptExistUser(Long deptId);
+
+    /**
+     * 校验部门名称是否唯一
+     * 
+     * @param deptName 部门名称
+     * @param parentId 父部门ID
+     * @return 结果
+     */
+    public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId);
+
+    /**
+     * 新增部门信息
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    public int insertDept(SysDept dept);
+
+    /**
+     * 修改部门信息
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    public int updateDept(SysDept dept);
+
+    /**
+     * 修改所在部门正常状态
+     * 
+     * @param deptIds 部门ID组
+     */
+    public void updateDeptStatusNormal(Long[] deptIds);
+
+    /**
+     * 修改子元素关系
+     * 
+     * @param depts 子元素
+     * @return 结果
+     */
+    public int updateDeptChildren(@Param("depts") List<SysDept> depts);
+
+    /**
+     * 删除部门管理信息
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    public int deleteDeptById(Long deptId);
+
+    IPage<SysDeptPageVO> page(@Param("page")IPage<SysDeptPageVO> page, @Param("name")String name);
+
+    List<SysDeptPageVO> getDeptList();
+
+    SysDept selectDeptByName(@Param("name")String name);
+
+    void updateDeptName(@Param("sysDept")SysDept sysDept);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java
new file mode 100644
index 0000000..a341f1e
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java
@@ -0,0 +1,95 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.common.core.domain.entity.SysDictData;
+
+/**
+ * 字典表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysDictDataMapper
+{
+    /**
+     * 根据条件分页查询字典数据
+     * 
+     * @param dictData 字典数据信息
+     * @return 字典数据集合信息
+     */
+    public List<SysDictData> selectDictDataList(SysDictData dictData);
+
+    /**
+     * 根据字典类型查询字典数据
+     * 
+     * @param dictType 字典类型
+     * @return 字典数据集合信息
+     */
+    public List<SysDictData> selectDictDataByType(String dictType);
+
+    /**
+     * 根据字典类型和字典键值查询字典数据信息
+     * 
+     * @param dictType 字典类型
+     * @param dictValue 字典键值
+     * @return 字典标签
+     */
+    public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue);
+
+    /**
+     * 根据字典数据ID查询信息
+     * 
+     * @param dictCode 字典数据ID
+     * @return 字典数据
+     */
+    public SysDictData selectDictDataById(Long dictCode);
+
+    /**
+     * 查询字典数据
+     * 
+     * @param dictType 字典类型
+     * @return 字典数据
+     */
+    public int countDictDataByType(String dictType);
+
+    /**
+     * 通过字典ID删除字典数据信息
+     * 
+     * @param dictCode 字典数据ID
+     * @return 结果
+     */
+    public int deleteDictDataById(Long dictCode);
+
+    /**
+     * 批量删除字典数据信息
+     * 
+     * @param dictCodes 需要删除的字典数据ID
+     * @return 结果
+     */
+    public int deleteDictDataByIds(Long[] dictCodes);
+
+    /**
+     * 新增字典数据信息
+     * 
+     * @param dictData 字典数据信息
+     * @return 结果
+     */
+    public int insertDictData(SysDictData dictData);
+
+    /**
+     * 修改字典数据信息
+     * 
+     * @param dictData 字典数据信息
+     * @return 结果
+     */
+    public int updateDictData(SysDictData dictData);
+
+    /**
+     * 同步修改字典类型
+     * 
+     * @param oldDictType 旧字典类型
+     * @param newDictType 新旧字典类型
+     * @return 结果
+     */
+    public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java
new file mode 100644
index 0000000..5fb48fb
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java
@@ -0,0 +1,83 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.common.core.domain.entity.SysDictType;
+
+/**
+ * 字典表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysDictTypeMapper
+{
+    /**
+     * 根据条件分页查询字典类型
+     * 
+     * @param dictType 字典类型信息
+     * @return 字典类型集合信息
+     */
+    public List<SysDictType> selectDictTypeList(SysDictType dictType);
+
+    /**
+     * 根据所有字典类型
+     * 
+     * @return 字典类型集合信息
+     */
+    public List<SysDictType> selectDictTypeAll();
+
+    /**
+     * 根据字典类型ID查询信息
+     * 
+     * @param dictId 字典类型ID
+     * @return 字典类型
+     */
+    public SysDictType selectDictTypeById(Long dictId);
+
+    /**
+     * 根据字典类型查询信息
+     * 
+     * @param dictType 字典类型
+     * @return 字典类型
+     */
+    public SysDictType selectDictTypeByType(String dictType);
+
+    /**
+     * 通过字典ID删除字典信息
+     * 
+     * @param dictId 字典ID
+     * @return 结果
+     */
+    public int deleteDictTypeById(Long dictId);
+
+    /**
+     * 批量删除字典类型信息
+     * 
+     * @param dictIds 需要删除的字典ID
+     * @return 结果
+     */
+    public int deleteDictTypeByIds(Long[] dictIds);
+
+    /**
+     * 新增字典类型信息
+     * 
+     * @param dictType 字典类型信息
+     * @return 结果
+     */
+    public int insertDictType(SysDictType dictType);
+
+    /**
+     * 修改字典类型信息
+     * 
+     * @param dictType 字典类型信息
+     * @return 结果
+     */
+    public int updateDictType(SysDictType dictType);
+
+    /**
+     * 校验字典类型称是否唯一
+     * 
+     * @param dictType 字典类型
+     * @return 结果
+     */
+    public SysDictType checkDictTypeUnique(String dictType);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java
new file mode 100644
index 0000000..629866f
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java
@@ -0,0 +1,42 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysLogininfor;
+
+/**
+ * 系统访问日志情况信息 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysLogininforMapper
+{
+    /**
+     * 新增系统登录日志
+     * 
+     * @param logininfor 访问日志对象
+     */
+    public void insertLogininfor(SysLogininfor logininfor);
+
+    /**
+     * 查询系统登录日志集合
+     * 
+     * @param logininfor 访问日志对象
+     * @return 登录记录集合
+     */
+    public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor);
+
+    /**
+     * 批量删除系统登录日志
+     * 
+     * @param infoIds 需要删除的登录日志ID
+     * @return 结果
+     */
+    public int deleteLogininforByIds(Long[] infoIds);
+
+    /**
+     * 清空系统登录日志
+     * 
+     * @return 结果
+     */
+    public int cleanLogininfor();
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
new file mode 100644
index 0000000..441f359
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
@@ -0,0 +1,131 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.system.object.vo.MenuTreeVO;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.common.core.domain.entity.SysMenu;
+
+/**
+ * 菜单表 数据层
+ *
+ * @author ruoyi
+ */
+public interface SysMenuMapper
+{
+    /**
+     * 查询系统菜单列表
+     *
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuList(SysMenu menu);
+
+    /**
+     * 根据用户所有权限
+     *
+     * @return 权限列表
+     */
+    public List<String> selectMenuPerms();
+
+    /**
+     * 根据用户查询系统菜单列表
+     *
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuListByUserId(SysMenu menu);
+
+    /**
+     * 根据角色ID查询权限
+     * 
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    public List<String> selectMenuPermsByRoleId(Long roleId);
+
+    /**
+     * 根据用户ID查询权限
+     *
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    public List<String> selectMenuPermsByUserId(Long userId);
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuTreeAll();
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuTreeByUserId(Long userId);
+
+    /**
+     * 根据角色ID查询菜单树信息
+     * 
+     * @param roleId 角色ID
+     * @param menuCheckStrictly 菜单树选择项是否关联显示
+     * @return 选中菜单列表
+     */
+    public List<Long> selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly);
+
+    /**
+     * 根据菜单ID查询信息
+     *
+     * @param menuId 菜单ID
+     * @return 菜单信息
+     */
+    public SysMenu selectMenuById(Long menuId);
+
+    /**
+     * 是否存在菜单子节点
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    public int hasChildByMenuId(Long menuId);
+
+    /**
+     * 新增菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public int insertMenu(SysMenu menu);
+
+    /**
+     * 修改菜单信息
+     *
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public int updateMenu(SysMenu menu);
+
+    /**
+     * 删除菜单管理信息
+     *
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    public int deleteMenuById(Long menuId);
+
+    /**
+     * 校验菜单名称是否唯一
+     *
+     * @param menuName 菜单名称
+     * @param parentId 父菜单ID
+     * @return 结果
+     */
+    public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId);
+
+    List<MenuTreeVO> getAllRootMenu();
+
+    List<MenuTreeVO> selectMenusByParentId(@Param("menuId") Long menuId);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java
new file mode 100644
index 0000000..0f0d7bc
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java
@@ -0,0 +1,62 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.system.domain.SysNotice;
+
+/**
+ * 通知公告表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysNoticeMapper extends BaseMapper<SysNotice>
+{
+    /**
+     * 查询公告信息
+     * 
+     * @param noticeId 公告ID
+     * @return 公告信息
+     */
+    public SysNotice selectNoticeById(Long noticeId);
+
+    /**
+     * 查询公告列表
+     * 
+     * @param notice 公告信息
+     * @return 公告集合
+     */
+    public List<SysNotice> selectNoticeList(SysNotice notice);
+
+    /**
+     * 新增公告
+     * 
+     * @param notice 公告信息
+     * @return 结果
+     */
+    public int insertNotice(SysNotice notice);
+
+    /**
+     * 修改公告
+     * 
+     * @param notice 公告信息
+     * @return 结果
+     */
+    public int updateNotice(SysNotice notice);
+
+    /**
+     * 批量删除公告
+     * 
+     * @param noticeId 公告ID
+     * @return 结果
+     */
+    public int deleteNoticeById(Long noticeId);
+
+    /**
+     * 批量删除公告信息
+     * 
+     * @param noticeIds 需要删除的公告ID
+     * @return 结果
+     */
+    public int deleteNoticeByIds(Long[] noticeIds);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java
new file mode 100644
index 0000000..2ae6457
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java
@@ -0,0 +1,48 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysOperLog;
+
+/**
+ * 操作日志 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysOperLogMapper
+{
+    /**
+     * 新增操作日志
+     * 
+     * @param operLog 操作日志对象
+     */
+    public void insertOperlog(SysOperLog operLog);
+
+    /**
+     * 查询系统操作日志集合
+     * 
+     * @param operLog 操作日志对象
+     * @return 操作日志集合
+     */
+    public List<SysOperLog> selectOperLogList(SysOperLog operLog);
+
+    /**
+     * 批量删除系统操作日志
+     * 
+     * @param operIds 需要删除的操作日志ID
+     * @return 结果
+     */
+    public int deleteOperLogByIds(Long[] operIds);
+
+    /**
+     * 查询操作日志详细
+     * 
+     * @param operId 操作ID
+     * @return 操作日志对象
+     */
+    public SysOperLog selectOperLogById(Long operId);
+
+    /**
+     * 清空操作日志
+     */
+    public void cleanOperLog();
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java
new file mode 100644
index 0000000..19be227
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java
@@ -0,0 +1,99 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysPost;
+
+/**
+ * 岗位信息 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysPostMapper
+{
+    /**
+     * 查询岗位数据集合
+     * 
+     * @param post 岗位信息
+     * @return 岗位数据集合
+     */
+    public List<SysPost> selectPostList(SysPost post);
+
+    /**
+     * 查询所有岗位
+     * 
+     * @return 岗位列表
+     */
+    public List<SysPost> selectPostAll();
+
+    /**
+     * 通过岗位ID查询岗位信息
+     * 
+     * @param postId 岗位ID
+     * @return 角色对象信息
+     */
+    public SysPost selectPostById(Long postId);
+
+    /**
+     * 根据用户ID获取岗位选择框列表
+     * 
+     * @param userId 用户ID
+     * @return 选中岗位ID列表
+     */
+    public List<Long> selectPostListByUserId(Long userId);
+
+    /**
+     * 查询用户所属岗位组
+     * 
+     * @param userName 用户名
+     * @return 结果
+     */
+    public List<SysPost> selectPostsByUserName(String userName);
+
+    /**
+     * 删除岗位信息
+     * 
+     * @param postId 岗位ID
+     * @return 结果
+     */
+    public int deletePostById(Long postId);
+
+    /**
+     * 批量删除岗位信息
+     * 
+     * @param postIds 需要删除的岗位ID
+     * @return 结果
+     */
+    public int deletePostByIds(Long[] postIds);
+
+    /**
+     * 修改岗位信息
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    public int updatePost(SysPost post);
+
+    /**
+     * 新增岗位信息
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    public int insertPost(SysPost post);
+
+    /**
+     * 校验岗位名称
+     * 
+     * @param postName 岗位名称
+     * @return 结果
+     */
+    public SysPost checkPostNameUnique(String postName);
+
+    /**
+     * 校验岗位编码
+     * 
+     * @param postCode 岗位编码
+     * @return 结果
+     */
+    public SysPost checkPostCodeUnique(String postCode);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java
new file mode 100644
index 0000000..f9d3a2f
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java
@@ -0,0 +1,44 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysRoleDept;
+
+/**
+ * 角色与部门关联表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysRoleDeptMapper
+{
+    /**
+     * 通过角色ID删除角色和部门关联
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int deleteRoleDeptByRoleId(Long roleId);
+
+    /**
+     * 批量删除角色部门关联信息
+     * 
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteRoleDept(Long[] ids);
+
+    /**
+     * 查询部门使用数量
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    public int selectCountRoleDeptByDeptId(Long deptId);
+
+    /**
+     * 批量新增角色部门信息
+     * 
+     * @param roleDeptList 角色部门列表
+     * @return 结果
+     */
+    public int batchRoleDept(List<SysRoleDept> roleDeptList);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java
new file mode 100644
index 0000000..2d3d4b7
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java
@@ -0,0 +1,114 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.system.object.vo.SysRolePageVO;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 角色表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysRoleMapper
+{
+    /**
+     * 根据条件分页查询角色数据
+     * 
+     * @param role 角色信息
+     * @return 角色数据集合信息
+     */
+    public List<SysRole> selectRoleList(SysRole role);
+
+    /**
+     * 根据用户ID查询角色
+     * 
+     * @param userId 用户ID
+     * @return 角色列表
+     */
+    public List<SysRole> selectRolePermissionByUserId(Long userId);
+
+    /**
+     * 查询所有角色
+     * 
+     * @return 角色列表
+     */
+    public List<SysRole> selectRoleAll();
+
+    /**
+     * 根据用户ID获取角色选择框列表
+     * 
+     * @param userId 用户ID
+     * @return 选中角色ID列表
+     */
+    public List<Long> selectRoleListByUserId(Long userId);
+
+    /**
+     * 通过角色ID查询角色
+     * 
+     * @param roleId 角色ID
+     * @return 角色对象信息
+     */
+    public SysRole selectRoleById(Long roleId);
+
+    /**
+     * 根据用户ID查询角色
+     * 
+     * @param userName 用户名
+     * @return 角色列表
+     */
+    public List<SysRole> selectRolesByUserName(String userName);
+
+    /**
+     * 校验角色名称是否唯一
+     * 
+     * @param roleName 角色名称
+     * @return 角色信息
+     */
+    public SysRole checkRoleNameUnique(String roleName);
+
+    /**
+     * 校验角色权限是否唯一
+     * 
+     * @param roleKey 角色权限
+     * @return 角色信息
+     */
+    public SysRole checkRoleKeyUnique(String roleKey);
+
+    /**
+     * 修改角色信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int updateRole(SysRole role);
+
+    /**
+     * 新增角色信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int insertRole(SysRole role);
+
+    /**
+     * 通过角色ID删除角色
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int deleteRoleById(Long roleId);
+
+    /**
+     * 批量删除角色信息
+     * 
+     * @param roleIds 需要删除的角色ID
+     * @return 结果
+     */
+    public int deleteRoleByIds(Long[] roleIds);
+
+    IPage<SysRolePageVO> page(IPage<SysRolePageVO> page, String name);
+
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java
new file mode 100644
index 0000000..1b0ca21
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java
@@ -0,0 +1,49 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysRoleMenu;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 角色与菜单关联表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysRoleMenuMapper
+{
+    /**
+     * 查询菜单使用数量
+     * 
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    public int checkMenuExistRole(Long menuId);
+
+    /**
+     * 通过角色ID删除角色和菜单关联
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int deleteRoleMenuByRoleId(Long roleId);
+
+    /**
+     * 批量删除角色菜单关联信息
+     * 
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteRoleMenu(Long[] ids);
+
+    /**
+     * 批量新增角色菜单信息
+     * 
+     * @param roleMenuList 角色菜单列表
+     * @return 结果
+     */
+    public int batchRoleMenu(List<SysRoleMenu> roleMenuList);
+
+    List<Long> selectMenuIdsByRoleIds(@Param("roleList") List<Long> roleList);
+
+    void insert(@Param("roleId") Long roleId, @Param("menuIds") List<Long> menuIds);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
new file mode 100644
index 0000000..6d7742c
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -0,0 +1,138 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.object.vo.SysUserPageListVO;
+import com.ruoyi.system.object.vo.SysUserVO;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.common.core.domain.entity.SysUser;
+
+/**
+ * 用户表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysUserMapper
+{
+    /**
+     * 根据条件分页查询用户列表
+     * 
+     * @param sysUser 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectUserList(SysUser sysUser);
+
+    /**
+     * 根据条件分页查询已配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectAllocatedList(SysUser user);
+
+    /**
+     * 根据条件分页查询未分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectUnallocatedList(SysUser user);
+
+    /**
+     * 通过用户名查询用户
+     * 
+     * @param userName 用户名
+     * @return 用户对象信息
+     */
+    public SysUser selectUserByUserName(String userName);
+
+    /**
+     * 通过用户ID查询用户
+     * 
+     * @param userId 用户ID
+     * @return 用户对象信息
+     */
+    public SysUser selectUserById(Long userId);
+
+    /**
+     * 新增用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public int insertUser(SysUser user);
+
+    /**
+     * 修改用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public int updateUser(SysUser user);
+
+    /**
+     * 修改用户头像
+     * 
+     * @param userName 用户名
+     * @param avatar 头像地址
+     * @return 结果
+     */
+    public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar);
+
+    /**
+     * 重置用户密码
+     * 
+     * @param userName 用户名
+     * @param password 密码
+     * @return 结果
+     */
+    public int resetUserPwd(@Param("userName") String userName, @Param("password") String password);
+
+    /**
+     * 通过用户ID删除用户
+     * 
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public int deleteUserById(Long userId);
+
+    /**
+     * 批量删除用户信息
+     * 
+     * @param userIds 需要删除的用户ID
+     * @return 结果
+     */
+    public int deleteUserByIds(Long[] userIds);
+
+    /**
+     * 校验用户名称是否唯一
+     * 
+     * @param userName 用户名称
+     * @return 结果
+     */
+    public SysUser checkUserNameUnique(String userName);
+
+    /**
+     * 校验手机号码是否唯一
+     *
+     * @param phonenumber 手机号码
+     * @return 结果
+     */
+    public SysUser checkPhoneUnique(String phonenumber);
+
+    /**
+     * 校验email是否唯一
+     *
+     * @param email 用户邮箱
+     * @return 结果
+     */
+    public SysUser checkEmailUnique(String email);
+
+    IPage<SysUserPageListVO> getSysUserPageList(@Param("page") IPage<SysUserPageListVO> page, @Param("nickName") String nickName,@Param("phone")  Integer phone, @Param("status") String status);
+
+    R<SysUserVO> getSysUserVO(@Param("userId") Long userId);
+
+    SysUser selectuserByPhone(@Param("phonenumber") String phonenumber);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java
new file mode 100644
index 0000000..2a6a720
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java
@@ -0,0 +1,44 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysUserPost;
+
+/**
+ * 用户与岗位关联表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysUserPostMapper
+{
+    /**
+     * 通过用户ID删除用户和岗位关联
+     * 
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public int deleteUserPostByUserId(Long userId);
+
+    /**
+     * 通过岗位ID查询岗位使用数量
+     * 
+     * @param postId 岗位ID
+     * @return 结果
+     */
+    public int countUserPostById(Long postId);
+
+    /**
+     * 批量删除用户和岗位关联
+     * 
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteUserPost(Long[] ids);
+
+    /**
+     * 批量新增用户岗位信息
+     * 
+     * @param userPostList 用户岗位列表
+     * @return 结果
+     */
+    public int batchUserPost(List<SysUserPost> userPostList);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java b/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java
new file mode 100644
index 0000000..739ae33
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java
@@ -0,0 +1,68 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.common.core.domain.entity.SysRole;
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.system.domain.SysUserRole;
+
+/**
+ * 用户与角色关联表 数据层
+ * 
+ * @author ruoyi
+ */
+public interface SysUserRoleMapper
+{
+    /**
+     * 通过用户ID删除用户和角色关联
+     * 
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public int deleteUserRoleByUserId(Long userId);
+
+    /**
+     * 批量删除用户和角色关联
+     * 
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteUserRole(Long[] ids);
+
+    /**
+     * 通过角色ID查询角色使用数量
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int countUserRoleByRoleId(Long roleId);
+
+    /**
+     * 批量新增用户角色信息
+     * 
+     * @param userRoleList 用户角色列表
+     * @return 结果
+     */
+    public int batchUserRole(List<SysUserRole> userRoleList);
+
+    /**
+     * 删除用户和角色关联信息
+     * 
+     * @param userRole 用户和角色关联信息
+     * @return 结果
+     */
+    public int deleteUserRoleInfo(SysUserRole userRole);
+
+    /**
+     * 批量取消授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要删除的用户数据ID
+     * @return 结果
+     */
+    public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds);
+
+    void insert(@Param("userId")Long userId, @Param("roleId")Long roleId);
+
+    SysRole selectRoleByUserId(@Param("userId")Long userId);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysRoleDTO.java b/pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysRoleDTO.java
new file mode 100644
index 0000000..72050ea
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysRoleDTO.java
@@ -0,0 +1,16 @@
+package com.ruoyi.system.object.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@ApiModel("添加角色DTO")
+public class AddSysRoleDTO {
+    @ApiModelProperty("角色名称")
+    private String roleName;
+    @ApiModelProperty("菜单权限集合")
+    private List<Long> menuIds;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysUserDTO.java b/pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysUserDTO.java
new file mode 100644
index 0000000..1efa2d5
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/dto/AddSysUserDTO.java
@@ -0,0 +1,19 @@
+package com.ruoyi.system.object.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("添加用户DTO")
+public class AddSysUserDTO{
+    @ApiModelProperty(value = "姓名")
+    private String nickName;
+    @ApiModelProperty(value = "联系电话")
+    private String phonenumber;
+    @ApiModelProperty(value = "所在部门id")
+    private Long deptId;
+    @ApiModelProperty(value = "所属角色id")
+    private Long roleId;
+
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/dto/EditSysRoleDTO.java b/pt-system/src/main/java/com/ruoyi/system/object/dto/EditSysRoleDTO.java
new file mode 100644
index 0000000..5c29339
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/dto/EditSysRoleDTO.java
@@ -0,0 +1,15 @@
+package com.ruoyi.system.object.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@ApiModel("编辑角色DTO")
+public class EditSysRoleDTO extends AddSysRoleDTO {
+    @ApiModelProperty("角色id")
+    private Long roleId;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/dto/SetPasswordDTO.java b/pt-system/src/main/java/com/ruoyi/system/object/dto/SetPasswordDTO.java
new file mode 100644
index 0000000..e55650d
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/dto/SetPasswordDTO.java
@@ -0,0 +1,15 @@
+package com.ruoyi.system.object.dto;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("重置密码DTO")
+public class SetPasswordDTO {
+    @ApiModelProperty("旧密码")
+    private String pwd;
+    @ApiModelProperty("新密码")
+    private String newPwd;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/vo/EditSysUserDTO.java b/pt-system/src/main/java/com/ruoyi/system/object/vo/EditSysUserDTO.java
new file mode 100644
index 0000000..334fbee
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/vo/EditSysUserDTO.java
@@ -0,0 +1,15 @@
+package com.ruoyi.system.object.vo;
+
+import com.ruoyi.system.object.dto.AddSysUserDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@ApiModel("编辑用户DTO")
+public class EditSysUserDTO extends AddSysUserDTO {
+    @ApiModelProperty(value = "用户id")
+    private Long userId;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/vo/MenuTreeVO.java b/pt-system/src/main/java/com/ruoyi/system/object/vo/MenuTreeVO.java
new file mode 100644
index 0000000..d2a2785
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/vo/MenuTreeVO.java
@@ -0,0 +1,30 @@
+package com.ruoyi.system.object.vo;
+
+import com.ruoyi.common.core.domain.entity.SysMenu;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@ApiModel("菜单树VO")
+public class MenuTreeVO {
+    /** 菜单ID */
+    @ApiModelProperty("菜单ID")
+    private Long menuId;
+    /** 菜单名称 */
+    @ApiModelProperty("菜单名称")
+    private String menuName;
+    /** 路由地址 */
+    @ApiModelProperty("路由地址")
+    private String path;
+    /** 类型(M目录 C菜单 F按钮) */
+    @ApiModelProperty("类型(M目录 C菜单 F按钮)")
+    private String menuType;
+    /** 子菜单 */
+    @ApiModelProperty("子菜单")
+    private List<MenuTreeVO> children = new ArrayList<MenuTreeVO>();
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/vo/SysDeptPageVO.java b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysDeptPageVO.java
new file mode 100644
index 0000000..284af71
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysDeptPageVO.java
@@ -0,0 +1,17 @@
+package com.ruoyi.system.object.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("部门分页VO")
+public class SysDeptPageVO {
+    @ApiModelProperty("部门id")
+    private Long deptId;
+
+    @ApiModelProperty("部门名称")
+    private String name;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/vo/SysRolePageVO.java b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysRolePageVO.java
new file mode 100644
index 0000000..cbc167c
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysRolePageVO.java
@@ -0,0 +1,18 @@
+package com.ruoyi.system.object.vo;
+
+import com.ruoyi.common.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("角色分页列表")
+public class SysRolePageVO {
+    @ApiModelProperty("角色序号")
+    private Long roleId;
+
+    @ApiModelProperty("角色名称")
+    private String roleName;
+
+
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/vo/SysRoleVO.java b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysRoleVO.java
new file mode 100644
index 0000000..1a10097
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysRoleVO.java
@@ -0,0 +1,18 @@
+package com.ruoyi.system.object.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@ApiModel("查看角色详情VO")
+public class SysRoleVO {
+    @ApiModelProperty("角色id")
+    private Long roleId;
+    @ApiModelProperty("角色名称")
+    private String roleName;
+    @ApiModelProperty("菜单权限集合")
+    private List<Long> menuIdList;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserPageListVO.java b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserPageListVO.java
new file mode 100644
index 0000000..525d895
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserPageListVO.java
@@ -0,0 +1,24 @@
+package com.ruoyi.system.object.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("用户账号分页列表VO")
+public class SysUserPageListVO {
+    @ApiModelProperty(value = "用户id")
+    private Long userId;
+    @ApiModelProperty(value = "用户名称")
+    private String nickName;
+    @ApiModelProperty(value = "联系电话")
+    private String phonenumber;
+
+    @ApiModelProperty(value = "所在部门")
+    private String deptName;
+
+    @ApiModelProperty(value = "所属角色")
+    private String roleName;
+    @ApiModelProperty(value = "账号状态 0=正常,1=停用")
+    private String status;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserVO.java b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserVO.java
new file mode 100644
index 0000000..72dd33a
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/object/vo/SysUserVO.java
@@ -0,0 +1,16 @@
+package com.ruoyi.system.object.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@ApiModel("用户账号分页列表VO")
+public class SysUserVO extends SysUserPageListVO{
+    @ApiModelProperty(value = "所在部门id")
+    private Long deptId;
+    @ApiModelProperty(value = "所属角色id")
+    private Long roleId;
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java
new file mode 100644
index 0000000..b307776
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java
@@ -0,0 +1,89 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysConfig;
+
+/**
+ * 参数配置 服务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysConfigService
+{
+    /**
+     * 查询参数配置信息
+     * 
+     * @param configId 参数配置ID
+     * @return 参数配置信息
+     */
+    public SysConfig selectConfigById(Long configId);
+
+    /**
+     * 根据键名查询参数配置信息
+     * 
+     * @param configKey 参数键名
+     * @return 参数键值
+     */
+    public String selectConfigByKey(String configKey);
+
+    /**
+     * 获取验证码开关
+     * 
+     * @return true开启,false关闭
+     */
+    public boolean selectCaptchaEnabled();
+
+    /**
+     * 查询参数配置列表
+     * 
+     * @param config 参数配置信息
+     * @return 参数配置集合
+     */
+    public List<SysConfig> selectConfigList(SysConfig config);
+
+    /**
+     * 新增参数配置
+     * 
+     * @param config 参数配置信息
+     * @return 结果
+     */
+    public int insertConfig(SysConfig config);
+
+    /**
+     * 修改参数配置
+     * 
+     * @param config 参数配置信息
+     * @return 结果
+     */
+    public int updateConfig(SysConfig config);
+
+    /**
+     * 批量删除参数信息
+     * 
+     * @param configIds 需要删除的参数ID
+     */
+    public void deleteConfigByIds(Long[] configIds);
+
+    /**
+     * 加载参数缓存数据
+     */
+    public void loadingConfigCache();
+
+    /**
+     * 清空参数缓存数据
+     */
+    public void clearConfigCache();
+
+    /**
+     * 重置参数缓存数据
+     */
+    public void resetConfigCache();
+
+    /**
+     * 校验参数键名是否唯一
+     * 
+     * @param config 参数信息
+     * @return 结果
+     */
+    public boolean checkConfigKeyUnique(SysConfig config);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
new file mode 100644
index 0000000..626571d
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
@@ -0,0 +1,135 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.common.core.domain.TreeSelect;
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.system.object.vo.SysDeptPageVO;
+
+/**
+ * 部门管理 服务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysDeptService
+{
+    /**
+     * 查询部门管理数据
+     * 
+     * @param dept 部门信息
+     * @return 部门信息集合
+     */
+    public List<SysDept> selectDeptList(SysDept dept);
+
+    /**
+     * 查询部门树结构信息
+     * 
+     * @param dept 部门信息
+     * @return 部门树信息集合
+     */
+    public List<TreeSelect> selectDeptTreeList(SysDept dept);
+
+    /**
+     * 构建前端所需要树结构
+     * 
+     * @param depts 部门列表
+     * @return 树结构列表
+     */
+    public List<SysDept> buildDeptTree(List<SysDept> depts);
+
+    /**
+     * 构建前端所需要下拉树结构
+     * 
+     * @param depts 部门列表
+     * @return 下拉树结构列表
+     */
+    public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts);
+
+    /**
+     * 根据角色ID查询部门树信息
+     * 
+     * @param roleId 角色ID
+     * @return 选中部门列表
+     */
+    public List<Long> selectDeptListByRoleId(Long roleId);
+
+    /**
+     * 根据部门ID查询信息
+     * 
+     * @param deptId 部门ID
+     * @return 部门信息
+     */
+    public SysDept selectDeptById(Long deptId);
+
+    /**
+     * 根据ID查询所有子部门(正常状态)
+     * 
+     * @param deptId 部门ID
+     * @return 子部门数
+     */
+    public int selectNormalChildrenDeptById(Long deptId);
+
+    /**
+     * 是否存在部门子节点
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    public boolean hasChildByDeptId(Long deptId);
+
+    /**
+     * 查询部门是否存在用户
+     * 
+     * @param deptId 部门ID
+     * @return 结果 true 存在 false 不存在
+     */
+    public boolean checkDeptExistUser(Long deptId);
+
+    /**
+     * 校验部门名称是否唯一
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    public boolean checkDeptNameUnique(SysDept dept);
+
+    /**
+     * 校验部门是否有数据权限
+     * 
+     * @param deptId 部门id
+     */
+    public void checkDeptDataScope(Long deptId);
+
+    /**
+     * 新增保存部门信息
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    public int insertDept(SysDept dept);
+
+    /**
+     * 修改保存部门信息
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    public int updateDept(SysDept dept);
+
+    /**
+     * 删除部门管理信息
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    public int deleteDeptById(Long deptId);
+
+    IPage<SysDeptPageVO> page(IPage<SysDeptPageVO> iPage, String name);
+
+    List<SysDeptPageVO> getDeptList();
+
+    void add(String name);
+
+    void updateDeptName(SysDept sysDept);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
new file mode 100644
index 0000000..868ba8c
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
@@ -0,0 +1,62 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.common.core.domain.entity.SysDictData;
+
+/**
+ * 字典 业务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysDictDataService
+{
+    /**
+     * 根据条件分页查询字典数据
+     * 
+     * @param dictData 字典数据信息
+     * @return 字典数据集合信息
+     */
+    public List<SysDictData> selectDictDataList(SysDictData dictData);
+
+    /**
+     * 根据字典类型和字典键值查询字典数据信息
+     * 
+     * @param dictType 字典类型
+     * @param dictValue 字典键值
+     * @return 字典标签
+     */
+    public String selectDictLabel(String dictType, String dictValue);
+
+    /**
+     * 根据字典数据ID查询信息
+     * 
+     * @param dictCode 字典数据ID
+     * @return 字典数据
+     */
+    public SysDictData selectDictDataById(Long dictCode);
+
+    /**
+     * 批量删除字典数据信息
+     * 
+     * @param dictCodes 需要删除的字典数据ID
+     */
+    public void deleteDictDataByIds(Long[] dictCodes);
+
+    /**
+     * 新增保存字典数据信息
+     * 
+     * @param dictData 字典数据信息
+     * @return 结果
+     */
+    public int insertDictData(SysDictData dictData);
+
+    /**
+     * 修改保存字典数据信息
+     * 
+     * @param dictData 字典数据信息
+     * @return 结果
+     */
+    public int updateDictData(SysDictData dictData);
+
+
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
new file mode 100644
index 0000000..01c1c1d
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
@@ -0,0 +1,98 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.common.core.domain.entity.SysDictData;
+import com.ruoyi.common.core.domain.entity.SysDictType;
+
+/**
+ * 字典 业务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysDictTypeService
+{
+    /**
+     * 根据条件分页查询字典类型
+     * 
+     * @param dictType 字典类型信息
+     * @return 字典类型集合信息
+     */
+    public List<SysDictType> selectDictTypeList(SysDictType dictType);
+
+    /**
+     * 根据所有字典类型
+     * 
+     * @return 字典类型集合信息
+     */
+    public List<SysDictType> selectDictTypeAll();
+
+    /**
+     * 根据字典类型查询字典数据
+     * 
+     * @param dictType 字典类型
+     * @return 字典数据集合信息
+     */
+    public List<SysDictData> selectDictDataByType(String dictType);
+
+    /**
+     * 根据字典类型ID查询信息
+     * 
+     * @param dictId 字典类型ID
+     * @return 字典类型
+     */
+    public SysDictType selectDictTypeById(Long dictId);
+
+    /**
+     * 根据字典类型查询信息
+     * 
+     * @param dictType 字典类型
+     * @return 字典类型
+     */
+    public SysDictType selectDictTypeByType(String dictType);
+
+    /**
+     * 批量删除字典信息
+     * 
+     * @param dictIds 需要删除的字典ID
+     */
+    public void deleteDictTypeByIds(Long[] dictIds);
+
+    /**
+     * 加载字典缓存数据
+     */
+    public void loadingDictCache();
+
+    /**
+     * 清空字典缓存数据
+     */
+    public void clearDictCache();
+
+    /**
+     * 重置字典缓存数据
+     */
+    public void resetDictCache();
+
+    /**
+     * 新增保存字典类型信息
+     * 
+     * @param dictType 字典类型信息
+     * @return 结果
+     */
+    public int insertDictType(SysDictType dictType);
+
+    /**
+     * 修改保存字典类型信息
+     * 
+     * @param dictType 字典类型信息
+     * @return 结果
+     */
+    public int updateDictType(SysDictType dictType);
+
+    /**
+     * 校验字典类型称是否唯一
+     * 
+     * @param dictType 字典类型
+     * @return 结果
+     */
+    public boolean checkDictTypeUnique(SysDictType dictType);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java
new file mode 100644
index 0000000..ce3151d
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java
@@ -0,0 +1,40 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysLogininfor;
+
+/**
+ * 系统访问日志情况信息 服务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysLogininforService
+{
+    /**
+     * 新增系统登录日志
+     * 
+     * @param logininfor 访问日志对象
+     */
+    public void insertLogininfor(SysLogininfor logininfor);
+
+    /**
+     * 查询系统登录日志集合
+     * 
+     * @param logininfor 访问日志对象
+     * @return 登录记录集合
+     */
+    public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor);
+
+    /**
+     * 批量删除系统登录日志
+     * 
+     * @param infoIds 需要删除的登录日志ID
+     * @return 结果
+     */
+    public int deleteLogininforByIds(Long[] infoIds);
+
+    /**
+     * 清空系统登录日志
+     */
+    public void cleanLogininfor();
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
new file mode 100644
index 0000000..88f3e65
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
@@ -0,0 +1,147 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import java.util.Set;
+import com.ruoyi.common.core.domain.TreeSelect;
+import com.ruoyi.common.core.domain.entity.SysMenu;
+import com.ruoyi.system.domain.vo.RouterVo;
+import com.ruoyi.system.object.vo.MenuTreeVO;
+
+/**
+ * 菜单 业务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysMenuService
+{
+    /**
+     * 根据用户查询系统菜单列表
+     * 
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuList(Long userId);
+
+    /**
+     * 根据用户查询系统菜单列表
+     * 
+     * @param menu 菜单信息
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuList(SysMenu menu, Long userId);
+
+    /**
+     * 根据用户ID查询权限
+     * 
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    public Set<String> selectMenuPermsByUserId(Long userId);
+
+    /**
+     * 根据角色ID查询权限
+     * 
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    public Set<String> selectMenuPermsByRoleId(Long roleId);
+
+    /**
+     * 根据用户ID查询菜单树信息
+     * 
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuTreeByUserId(Long userId);
+
+    /**
+     * 根据角色ID查询菜单树信息
+     * 
+     * @param roleId 角色ID
+     * @return 选中菜单列表
+     */
+    public List<Long> selectMenuListByRoleId(Long roleId);
+
+    /**
+     * 构建前端路由所需要的菜单
+     * 
+     * @param menus 菜单列表
+     * @return 路由列表
+     */
+    public List<RouterVo> buildMenus(List<SysMenu> menus);
+
+    /**
+     * 构建前端所需要树结构
+     * 
+     * @param menus 菜单列表
+     * @return 树结构列表
+     */
+    public List<SysMenu> buildMenuTree(List<SysMenu> menus);
+
+    /**
+     * 构建前端所需要下拉树结构
+     * 
+     * @param menus 菜单列表
+     * @return 下拉树结构列表
+     */
+    public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus);
+
+    /**
+     * 根据菜单ID查询信息
+     * 
+     * @param menuId 菜单ID
+     * @return 菜单信息
+     */
+    public SysMenu selectMenuById(Long menuId);
+
+    /**
+     * 是否存在菜单子节点
+     * 
+     * @param menuId 菜单ID
+     * @return 结果 true 存在 false 不存在
+     */
+    public boolean hasChildByMenuId(Long menuId);
+
+    /**
+     * 查询菜单是否存在角色
+     * 
+     * @param menuId 菜单ID
+     * @return 结果 true 存在 false 不存在
+     */
+    public boolean checkMenuExistRole(Long menuId);
+
+    /**
+     * 新增保存菜单信息
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public int insertMenu(SysMenu menu);
+
+    /**
+     * 修改保存菜单信息
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public int updateMenu(SysMenu menu);
+
+    /**
+     * 删除菜单管理信息
+     * 
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    public int deleteMenuById(Long menuId);
+
+    /**
+     * 校验菜单名称是否唯一
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public boolean checkMenuNameUnique(SysMenu menu);
+
+    List<MenuTreeVO> tree(Long userId);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
new file mode 100644
index 0000000..4c95884
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
@@ -0,0 +1,79 @@
+package com.ruoyi.system.service;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.system.domain.SysNotice;
+
+/**
+ * 公告 服务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysNoticeService extends IService<SysNotice>
+{
+    /**
+     * 查询公告信息
+     * 
+     * @param noticeId 公告ID
+     * @return 公告信息
+     */
+    public SysNotice selectNoticeById(Long noticeId);
+
+    /**
+     * 查询公告列表
+     * 
+     * @param notice 公告信息
+     * @return 公告集合
+     */
+    public List<SysNotice> selectNoticeList(SysNotice notice);
+
+    /**
+     * 新增公告
+     * 
+     * @param notice 公告信息
+     * @return 结果
+     */
+    public int insertNotice(SysNotice notice);
+
+    /**
+     * 修改公告
+     * 
+     * @param notice 公告信息
+     * @return 结果
+     */
+    public int updateNotice(SysNotice notice);
+
+    /**
+     * 删除公告信息
+     * 
+     * @param noticeId 公告ID
+     * @return 结果
+     */
+    public int deleteNoticeById(Long noticeId);
+    
+    /**
+     * 批量删除公告信息
+     * 
+     * @param noticeIds 需要删除的公告ID
+     * @return 结果
+     */
+    public int deleteNoticeByIds(Long[] noticeIds);
+
+
+    /**
+     * 常驻消息(<=0不显示)
+     * @return
+     */
+    public List<SysNotice> alwaysMessage();
+
+
+    /**
+     * 消息
+     * @return
+     */
+    public List<SysNotice> payMessage();
+
+
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java
new file mode 100644
index 0000000..4fd8e5a
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java
@@ -0,0 +1,48 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysOperLog;
+
+/**
+ * 操作日志 服务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysOperLogService
+{
+    /**
+     * 新增操作日志
+     * 
+     * @param operLog 操作日志对象
+     */
+    public void insertOperlog(SysOperLog operLog);
+
+    /**
+     * 查询系统操作日志集合
+     * 
+     * @param operLog 操作日志对象
+     * @return 操作日志集合
+     */
+    public List<SysOperLog> selectOperLogList(SysOperLog operLog);
+
+    /**
+     * 批量删除系统操作日志
+     * 
+     * @param operIds 需要删除的操作日志ID
+     * @return 结果
+     */
+    public int deleteOperLogByIds(Long[] operIds);
+
+    /**
+     * 查询操作日志详细
+     * 
+     * @param operId 操作ID
+     * @return 操作日志对象
+     */
+    public SysOperLog selectOperLogById(Long operId);
+
+    /**
+     * 清空操作日志
+     */
+    public void cleanOperLog();
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysPostService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysPostService.java
new file mode 100644
index 0000000..84779bf
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysPostService.java
@@ -0,0 +1,99 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysPost;
+
+/**
+ * 岗位信息 服务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysPostService
+{
+    /**
+     * 查询岗位信息集合
+     * 
+     * @param post 岗位信息
+     * @return 岗位列表
+     */
+    public List<SysPost> selectPostList(SysPost post);
+
+    /**
+     * 查询所有岗位
+     * 
+     * @return 岗位列表
+     */
+    public List<SysPost> selectPostAll();
+
+    /**
+     * 通过岗位ID查询岗位信息
+     * 
+     * @param postId 岗位ID
+     * @return 角色对象信息
+     */
+    public SysPost selectPostById(Long postId);
+
+    /**
+     * 根据用户ID获取岗位选择框列表
+     * 
+     * @param userId 用户ID
+     * @return 选中岗位ID列表
+     */
+    public List<Long> selectPostListByUserId(Long userId);
+
+    /**
+     * 校验岗位名称
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    public boolean checkPostNameUnique(SysPost post);
+
+    /**
+     * 校验岗位编码
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    public boolean checkPostCodeUnique(SysPost post);
+
+    /**
+     * 通过岗位ID查询岗位使用数量
+     * 
+     * @param postId 岗位ID
+     * @return 结果
+     */
+    public int countUserPostById(Long postId);
+
+    /**
+     * 删除岗位信息
+     * 
+     * @param postId 岗位ID
+     * @return 结果
+     */
+    public int deletePostById(Long postId);
+
+    /**
+     * 批量删除岗位信息
+     * 
+     * @param postIds 需要删除的岗位ID
+     * @return 结果
+     */
+    public int deletePostByIds(Long[] postIds);
+
+    /**
+     * 新增保存岗位信息
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    public int insertPost(SysPost post);
+
+    /**
+     * 修改保存岗位信息
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    public int updatePost(SysPost post);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
new file mode 100644
index 0000000..9788dcd
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
@@ -0,0 +1,189 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import java.util.Set;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.system.domain.SysUserRole;
+import com.ruoyi.system.object.dto.AddSysRoleDTO;
+import com.ruoyi.system.object.dto.EditSysRoleDTO;
+import com.ruoyi.system.object.vo.SysRolePageVO;
+import com.ruoyi.system.object.vo.SysRoleVO;
+
+import javax.validation.Valid;
+
+/**
+ * 角色业务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysRoleService
+{
+    /**
+     * 根据条件分页查询角色数据
+     * 
+     * @param role 角色信息
+     * @return 角色数据集合信息
+     */
+    public List<SysRole> selectRoleList(SysRole role);
+
+    /**
+     * 根据用户ID查询角色列表
+     * 
+     * @param userId 用户ID
+     * @return 角色列表
+     */
+    public List<SysRole> selectRolesByUserId(Long userId);
+
+    /**
+     * 根据用户ID查询角色权限
+     * 
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    public Set<String> selectRolePermissionByUserId(Long userId);
+
+    /**
+     * 查询所有角色
+     * 
+     * @return 角色列表
+     */
+    public List<SysRole> selectRoleAll();
+
+    /**
+     * 根据用户ID获取角色选择框列表
+     * 
+     * @param userId 用户ID
+     * @return 选中角色ID列表
+     */
+    public List<Long> selectRoleListByUserId(Long userId);
+
+    /**
+     * 通过角色ID查询角色
+     * 
+     * @param roleId 角色ID
+     * @return 角色对象信息
+     */
+    public SysRole selectRoleById(Long roleId);
+
+    /**
+     * 校验角色名称是否唯一
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public boolean checkRoleNameUnique(SysRole role);
+
+    /**
+     * 校验角色权限是否唯一
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public boolean checkRoleKeyUnique(SysRole role);
+
+    /**
+     * 校验角色是否允许操作
+     * 
+     * @param role 角色信息
+     */
+    public void checkRoleAllowed(SysRole role);
+
+    /**
+     * 校验角色是否有数据权限
+     * 
+     * @param roleIds 角色id
+     */
+    public void checkRoleDataScope(Long... roleIds);
+
+    /**
+     * 通过角色ID查询角色使用数量
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int countUserRoleByRoleId(Long roleId);
+
+    /**
+     * 新增保存角色信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int insertRole(SysRole role);
+
+    /**
+     * 修改保存角色信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int updateRole(SysRole role);
+
+    /**
+     * 修改角色状态
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int updateRoleStatus(SysRole role);
+
+    /**
+     * 修改数据权限信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    public int authDataScope(SysRole role);
+
+    /**
+     * 通过角色ID删除角色
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    public int deleteRoleById(Long roleId);
+
+    /**
+     * 批量删除角色信息
+     * 
+     * @param roleIds 需要删除的角色ID
+     * @return 结果
+     */
+    public int deleteRoleByIds(Long[] roleIds);
+
+    /**
+     * 取消授权用户角色
+     * 
+     * @param userRole 用户和角色关联信息
+     * @return 结果
+     */
+    public int deleteAuthUser(SysUserRole userRole);
+
+    /**
+     * 批量取消授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要取消授权的用户数据ID
+     * @return 结果
+     */
+    public int deleteAuthUsers(Long roleId, Long[] userIds);
+
+    /**
+     * 批量选择授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要删除的用户数据ID
+     * @return 结果
+     */
+    public int insertAuthUsers(Long roleId, Long[] userIds);
+
+    IPage<SysRolePageVO> page(IPage<SysRolePageVO> page, String name);
+
+    void add( AddSysRoleDTO dto);
+
+    void edit( EditSysRoleDTO dto);
+
+    SysRoleVO getById(Long roleId);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java
new file mode 100644
index 0000000..8eb5448
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java
@@ -0,0 +1,48 @@
+package com.ruoyi.system.service;
+
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.system.domain.SysUserOnline;
+
+/**
+ * 在线用户 服务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysUserOnlineService
+{
+    /**
+     * 通过登录地址查询信息
+     * 
+     * @param ipaddr 登录地址
+     * @param user 用户信息
+     * @return 在线用户信息
+     */
+    public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user);
+
+    /**
+     * 通过用户名称查询信息
+     * 
+     * @param userName 用户名称
+     * @param user 用户信息
+     * @return 在线用户信息
+     */
+    public SysUserOnline selectOnlineByUserName(String userName, LoginUser user);
+
+    /**
+     * 通过登录地址/用户名称查询信息
+     * 
+     * @param ipaddr 登录地址
+     * @param userName 用户名称
+     * @param user 用户信息
+     * @return 在线用户信息
+     */
+    public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user);
+
+    /**
+     * 设置在线用户信息
+     * 
+     * @param user 用户信息
+     * @return 在线用户
+     */
+    public SysUserOnline loginUserToUserOnline(LoginUser user);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/pt-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
new file mode 100644
index 0000000..807ed7e
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -0,0 +1,227 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.domain.entity.SysUser;
+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 javax.validation.Valid;
+
+/**
+ * 用户 业务层
+ * 
+ * @author ruoyi
+ */
+public interface ISysUserService
+{
+    /**
+     * 根据条件分页查询用户列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectUserList(SysUser user);
+
+    /**
+     * 根据条件分页查询已分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectAllocatedList(SysUser user);
+
+    /**
+     * 根据条件分页查询未分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    public List<SysUser> selectUnallocatedList(SysUser user);
+
+    /**
+     * 通过用户名查询用户
+     * 
+     * @param userName 用户名
+     * @return 用户对象信息
+     */
+    public SysUser selectUserByUserName(String userName);
+
+    /**
+     * 通过用户ID查询用户
+     * 
+     * @param userId 用户ID
+     * @return 用户对象信息
+     */
+    public SysUser selectUserById(Long userId);
+
+    /**
+     * 根据用户ID查询用户所属角色组
+     * 
+     * @param userName 用户名
+     * @return 结果
+     */
+    public String selectUserRoleGroup(String userName);
+
+    /**
+     * 根据用户ID查询用户所属岗位组
+     * 
+     * @param userName 用户名
+     * @return 结果
+     */
+    public String selectUserPostGroup(String userName);
+
+    /**
+     * 校验用户名称是否唯一
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public boolean checkUserNameUnique(SysUser user);
+
+    /**
+     * 校验手机号码是否唯一
+     *
+     * @param user 用户信息
+     * @return 结果
+     */
+    public boolean checkPhoneUnique(SysUser user);
+
+    /**
+     * 校验email是否唯一
+     *
+     * @param user 用户信息
+     * @return 结果
+     */
+    public boolean checkEmailUnique(SysUser user);
+
+    /**
+     * 校验用户是否允许操作
+     * 
+     * @param user 用户信息
+     */
+    public void checkUserAllowed(SysUser user);
+
+    /**
+     * 校验用户是否有数据权限
+     * 
+     * @param userId 用户id
+     */
+    public void checkUserDataScope(Long userId);
+
+    /**
+     * 新增用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public int insertUser(SysUser user);
+
+    /**
+     * 注册用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public boolean registerUser(SysUser user);
+
+    /**
+     * 修改用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public int updateUser(SysUser user);
+
+    /**
+     * 用户授权角色
+     * 
+     * @param userId 用户ID
+     * @param roleIds 角色组
+     */
+    public void insertUserAuth(Long userId, Long[] roleIds);
+
+    /**
+     * 修改用户状态
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public int updateUserStatus(SysUser user);
+
+    /**
+     * 修改用户基本信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public int updateUserProfile(SysUser user);
+
+    /**
+     * 修改用户头像
+     * 
+     * @param userName 用户名
+     * @param avatar 头像地址
+     * @return 结果
+     */
+    public boolean updateUserAvatar(String userName, String avatar);
+
+    /**
+     * 重置用户密码
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    public int resetPwd(SysUser user);
+
+    /**
+     * 重置用户密码
+     * 
+     * @param userName 用户名
+     * @param password 密码
+     * @return 结果
+     */
+    public int resetUserPwd(String userName, String password);
+
+    /**
+     * 通过用户ID删除用户
+     * 
+     * @param userId 用户ID
+     * @return 结果
+     */
+    public int deleteUserById(Long userId);
+
+    /**
+     * 批量删除用户信息
+     * 
+     * @param userIds 需要删除的用户ID
+     * @return 结果
+     */
+    public int deleteUserByIds(Long[] userIds);
+
+    /**
+     * 导入用户数据
+     * 
+     * @param userList 用户数据列表
+     * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
+     * @param operName 操作用户
+     * @return 结果
+     */
+    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
+
+    IPage<SysUserPageListVO> getSysUserPageList(IPage<SysUserPageListVO> page, String nickName, Integer phone, String status);
+
+    R<SysUserVO> getInfo(Long userId);
+
+    void add( AddSysUserDTO dto);
+
+    void edit( EditSysUserDTO dto);
+
+    void resetPassword(Long userId);
+
+    void changeStatus(Long userId);
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
new file mode 100644
index 0000000..4d29b22
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
@@ -0,0 +1,232 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.annotation.DataSource;
+import com.ruoyi.common.constant.CacheConstants;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.enums.DataSourceType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.SysConfig;
+import com.ruoyi.system.mapper.SysConfigMapper;
+import com.ruoyi.system.service.ISysConfigService;
+
+/**
+ * 参数配置 服务层实现
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysConfigServiceImpl implements ISysConfigService
+{
+    @Autowired
+    private SysConfigMapper configMapper;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 项目启动时,初始化参数到缓存
+     */
+    @PostConstruct
+    public void init()
+    {
+        loadingConfigCache();
+    }
+
+    /**
+     * 查询参数配置信息
+     * 
+     * @param configId 参数配置ID
+     * @return 参数配置信息
+     */
+    @Override
+    @DataSource(DataSourceType.MASTER)
+    public SysConfig selectConfigById(Long configId)
+    {
+        SysConfig config = new SysConfig();
+        config.setConfigId(configId);
+        return configMapper.selectConfig(config);
+    }
+
+    /**
+     * 根据键名查询参数配置信息
+     * 
+     * @param configKey 参数key
+     * @return 参数键值
+     */
+    @Override
+    public String selectConfigByKey(String configKey)
+    {
+        String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
+        if (StringUtils.isNotEmpty(configValue))
+        {
+            return configValue;
+        }
+        SysConfig config = new SysConfig();
+        config.setConfigKey(configKey);
+        SysConfig retConfig = configMapper.selectConfig(config);
+        if (StringUtils.isNotNull(retConfig))
+        {
+            redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue());
+            return retConfig.getConfigValue();
+        }
+        return StringUtils.EMPTY;
+    }
+
+    /**
+     * 获取验证码开关
+     * 
+     * @return true开启,false关闭
+     */
+    @Override
+    public boolean selectCaptchaEnabled()
+    {
+        String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled");
+        if (StringUtils.isEmpty(captchaEnabled))
+        {
+            return true;
+        }
+        return Convert.toBool(captchaEnabled);
+    }
+
+    /**
+     * 查询参数配置列表
+     * 
+     * @param config 参数配置信息
+     * @return 参数配置集合
+     */
+    @Override
+    public List<SysConfig> selectConfigList(SysConfig config)
+    {
+        return configMapper.selectConfigList(config);
+    }
+
+    /**
+     * 新增参数配置
+     * 
+     * @param config 参数配置信息
+     * @return 结果
+     */
+    @Override
+    public int insertConfig(SysConfig config)
+    {
+        int row = configMapper.insertConfig(config);
+        if (row > 0)
+        {
+            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
+        }
+        return row;
+    }
+
+    /**
+     * 修改参数配置
+     * 
+     * @param config 参数配置信息
+     * @return 结果
+     */
+    @Override
+    public int updateConfig(SysConfig config)
+    {
+        SysConfig temp = configMapper.selectConfigById(config.getConfigId());
+        if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey()))
+        {
+            redisCache.deleteObject(getCacheKey(temp.getConfigKey()));
+        }
+
+        int row = configMapper.updateConfig(config);
+        if (row > 0)
+        {
+            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
+        }
+        return row;
+    }
+
+    /**
+     * 批量删除参数信息
+     * 
+     * @param configIds 需要删除的参数ID
+     */
+    @Override
+    public void deleteConfigByIds(Long[] configIds)
+    {
+        for (Long configId : configIds)
+        {
+            SysConfig config = selectConfigById(configId);
+            if (StringUtils.equals(UserConstants.YES, config.getConfigType()))
+            {
+                throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey()));
+            }
+            configMapper.deleteConfigById(configId);
+            redisCache.deleteObject(getCacheKey(config.getConfigKey()));
+        }
+    }
+
+    /**
+     * 加载参数缓存数据
+     */
+    @Override
+    public void loadingConfigCache()
+    {
+        List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig());
+        for (SysConfig config : configsList)
+        {
+            redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
+        }
+    }
+
+    /**
+     * 清空参数缓存数据
+     */
+    @Override
+    public void clearConfigCache()
+    {
+        Collection<String> keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*");
+        redisCache.deleteObject(keys);
+    }
+
+    /**
+     * 重置参数缓存数据
+     */
+    @Override
+    public void resetConfigCache()
+    {
+        clearConfigCache();
+        loadingConfigCache();
+    }
+
+    /**
+     * 校验参数键名是否唯一
+     * 
+     * @param config 参数配置信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkConfigKeyUnique(SysConfig config)
+    {
+        Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId();
+        SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey());
+        if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 设置cache key
+     * 
+     * @param configKey 参数键
+     * @return 缓存键key
+     */
+    private String getCacheKey(String configKey)
+    {
+        return CacheConstants.SYS_CONFIG_KEY + configKey;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
new file mode 100644
index 0000000..63eaa26
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -0,0 +1,369 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.system.object.vo.SysDeptPageVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.TreeSelect;
+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.text.Convert;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.system.mapper.SysDeptMapper;
+import com.ruoyi.system.mapper.SysRoleMapper;
+import com.ruoyi.system.service.ISysDeptService;
+
+/**
+ * 部门管理 服务实现
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysDeptServiceImpl implements ISysDeptService
+{
+    @Autowired
+    private SysDeptMapper deptMapper;
+
+    @Autowired
+    private SysRoleMapper roleMapper;
+
+    /**
+     * 查询部门管理数据
+     * 
+     * @param dept 部门信息
+     * @return 部门信息集合
+     */
+    @Override
+    @DataScope(deptAlias = "d")
+    public List<SysDept> selectDeptList(SysDept dept)
+    {
+        return deptMapper.selectDeptList(dept);
+    }
+
+    /**
+     * 查询部门树结构信息
+     * 
+     * @param dept 部门信息
+     * @return 部门树信息集合
+     */
+    @Override
+    public List<TreeSelect> selectDeptTreeList(SysDept dept)
+    {
+        List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
+        return buildDeptTreeSelect(depts);
+    }
+
+    /**
+     * 构建前端所需要树结构
+     * 
+     * @param depts 部门列表
+     * @return 树结构列表
+     */
+    @Override
+    public List<SysDept> buildDeptTree(List<SysDept> depts)
+    {
+        List<SysDept> returnList = new ArrayList<SysDept>();
+        List<Long> tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList());
+        for (SysDept dept : depts)
+        {
+            // 如果是顶级节点, 遍历该父节点的所有子节点
+            if (!tempList.contains(dept.getParentId()))
+            {
+                recursionFn(depts, dept);
+                returnList.add(dept);
+            }
+        }
+        if (returnList.isEmpty())
+        {
+            returnList = depts;
+        }
+        return returnList;
+    }
+
+    /**
+     * 构建前端所需要下拉树结构
+     * 
+     * @param depts 部门列表
+     * @return 下拉树结构列表
+     */
+    @Override
+    public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts)
+    {
+        List<SysDept> deptTrees = buildDeptTree(depts);
+        return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
+    }
+
+    /**
+     * 根据角色ID查询部门树信息
+     * 
+     * @param roleId 角色ID
+     * @return 选中部门列表
+     */
+    @Override
+    public List<Long> selectDeptListByRoleId(Long roleId)
+    {
+        SysRole role = roleMapper.selectRoleById(roleId);
+        return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly());
+    }
+
+    /**
+     * 根据部门ID查询信息
+     * 
+     * @param deptId 部门ID
+     * @return 部门信息
+     */
+    @Override
+    public SysDept selectDeptById(Long deptId)
+    {
+        return deptMapper.selectDeptById(deptId);
+    }
+
+    /**
+     * 根据ID查询所有子部门(正常状态)
+     * 
+     * @param deptId 部门ID
+     * @return 子部门数
+     */
+    @Override
+    public int selectNormalChildrenDeptById(Long deptId)
+    {
+        return deptMapper.selectNormalChildrenDeptById(deptId);
+    }
+
+    /**
+     * 是否存在子节点
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    @Override
+    public boolean hasChildByDeptId(Long deptId)
+    {
+        int result = deptMapper.hasChildByDeptId(deptId);
+        return result > 0;
+    }
+
+    /**
+     * 查询部门是否存在用户
+     * 
+     * @param deptId 部门ID
+     * @return 结果 true 存在 false 不存在
+     */
+    @Override
+    public boolean checkDeptExistUser(Long deptId)
+    {
+        int result = deptMapper.checkDeptExistUser(deptId);
+        return result > 0;
+    }
+
+    /**
+     * 校验部门名称是否唯一
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkDeptNameUnique(SysDept dept)
+    {
+        Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId();
+        SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId());
+        if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验部门是否有数据权限
+     * 
+     * @param deptId 部门id
+     */
+    @Override
+    public void checkDeptDataScope(Long deptId)
+    {
+        if (!SysUser.isAdmin(SecurityUtils.getUserId()) && StringUtils.isNotNull(deptId))
+        {
+            SysDept dept = new SysDept();
+            dept.setDeptId(deptId);
+            List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
+            if (StringUtils.isEmpty(depts))
+            {
+                throw new ServiceException("没有权限访问部门数据!");
+            }
+        }
+    }
+
+    /**
+     * 新增保存部门信息
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    @Override
+    public int insertDept(SysDept dept)
+    {
+        SysDept info = deptMapper.selectDeptById(dept.getParentId());
+        // 如果父节点不为正常状态,则不允许新增子节点
+        if (!UserConstants.DEPT_NORMAL.equals(info.getStatus()))
+        {
+            throw new ServiceException("部门停用,不允许新增");
+        }
+        dept.setAncestors(info.getAncestors() + "," + dept.getParentId());
+        return deptMapper.insertDept(dept);
+    }
+
+    /**
+     * 修改保存部门信息
+     * 
+     * @param dept 部门信息
+     * @return 结果
+     */
+    @Override
+    public int updateDept(SysDept dept)
+    {
+        SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId());
+        SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId());
+        if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept))
+        {
+            String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId();
+            String oldAncestors = oldDept.getAncestors();
+            dept.setAncestors(newAncestors);
+            updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);
+        }
+        int result = deptMapper.updateDept(dept);
+        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())
+                && !StringUtils.equals("0", dept.getAncestors()))
+        {
+            // 如果该部门是启用状态,则启用该部门的所有上级部门
+            updateParentDeptStatusNormal(dept);
+        }
+        return result;
+    }
+
+    /**
+     * 修改该部门的父级部门状态
+     * 
+     * @param dept 当前部门
+     */
+    private void updateParentDeptStatusNormal(SysDept dept)
+    {
+        String ancestors = dept.getAncestors();
+        Long[] deptIds = Convert.toLongArray(ancestors);
+        deptMapper.updateDeptStatusNormal(deptIds);
+    }
+
+    /**
+     * 修改子元素关系
+     * 
+     * @param deptId 被修改的部门ID
+     * @param newAncestors 新的父ID集合
+     * @param oldAncestors 旧的父ID集合
+     */
+    public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors)
+    {
+        List<SysDept> children = deptMapper.selectChildrenDeptById(deptId);
+        for (SysDept child : children)
+        {
+            child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
+        }
+        if (children.size() > 0)
+        {
+            deptMapper.updateDeptChildren(children);
+        }
+    }
+
+    /**
+     * 删除部门管理信息
+     * 
+     * @param deptId 部门ID
+     * @return 结果
+     */
+    @Override
+    public int deleteDeptById(Long deptId)
+    {
+        return deptMapper.deleteDeptById(deptId);
+    }
+
+    @Override
+    public IPage<SysDeptPageVO> page(IPage<SysDeptPageVO> iPage, String name) {
+        return deptMapper.page(iPage,name);
+    }
+
+    @Override
+    public List<SysDeptPageVO> getDeptList() {
+        return deptMapper.getDeptList();
+    }
+
+    @Override
+    public void add(String name) {
+        SysDept sysDept=deptMapper.selectDeptByName(name);
+        if (sysDept != null){
+            throw new ServiceException("该部门已存在");
+        }
+        sysDept=new SysDept();
+        sysDept.setDeptName(name);
+        deptMapper.insertDept(sysDept);
+
+    }
+
+    @Override
+    public void updateDeptName(SysDept sysDept) {
+        deptMapper.updateDeptName(sysDept);
+    }
+
+    /**
+     * 递归列表
+     */
+    private void recursionFn(List<SysDept> list, SysDept t)
+    {
+        // 得到子节点列表
+        List<SysDept> childList = getChildList(list, t);
+        t.setChildren(childList);
+        for (SysDept tChild : childList)
+        {
+            if (hasChild(list, tChild))
+            {
+                recursionFn(list, tChild);
+            }
+        }
+    }
+
+    /**
+     * 得到子节点列表
+     */
+    private List<SysDept> getChildList(List<SysDept> list, SysDept t)
+    {
+        List<SysDept> tlist = new ArrayList<SysDept>();
+        Iterator<SysDept> it = list.iterator();
+        while (it.hasNext())
+        {
+            SysDept n = (SysDept) it.next();
+            if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue())
+            {
+                tlist.add(n);
+            }
+        }
+        return tlist;
+    }
+
+    /**
+     * 判断是否有子节点
+     */
+    private boolean hasChild(List<SysDept> list, SysDept t)
+    {
+        return getChildList(list, t).size() > 0;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
new file mode 100644
index 0000000..3cd85f5
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
@@ -0,0 +1,112 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.core.domain.entity.SysDictData;
+import com.ruoyi.common.utils.DictUtils;
+import com.ruoyi.system.mapper.SysDictDataMapper;
+import com.ruoyi.system.service.ISysDictDataService;
+
+/**
+ * 字典 业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysDictDataServiceImpl implements ISysDictDataService
+{
+    @Autowired
+    private SysDictDataMapper dictDataMapper;
+
+    /**
+     * 根据条件分页查询字典数据
+     * 
+     * @param dictData 字典数据信息
+     * @return 字典数据集合信息
+     */
+    @Override
+    public List<SysDictData> selectDictDataList(SysDictData dictData)
+    {
+        return dictDataMapper.selectDictDataList(dictData);
+    }
+
+    /**
+     * 根据字典类型和字典键值查询字典数据信息
+     * 
+     * @param dictType 字典类型
+     * @param dictValue 字典键值
+     * @return 字典标签
+     */
+    @Override
+    public String selectDictLabel(String dictType, String dictValue)
+    {
+        return dictDataMapper.selectDictLabel(dictType, dictValue);
+    }
+
+    /**
+     * 根据字典数据ID查询信息
+     * 
+     * @param dictCode 字典数据ID
+     * @return 字典数据
+     */
+    @Override
+    public SysDictData selectDictDataById(Long dictCode)
+    {
+        return dictDataMapper.selectDictDataById(dictCode);
+    }
+
+    /**
+     * 批量删除字典数据信息
+     * 
+     * @param dictCodes 需要删除的字典数据ID
+     */
+    @Override
+    public void deleteDictDataByIds(Long[] dictCodes)
+    {
+        for (Long dictCode : dictCodes)
+        {
+            SysDictData data = selectDictDataById(dictCode);
+            dictDataMapper.deleteDictDataById(dictCode);
+            List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(data.getDictType());
+            DictUtils.setDictCache(data.getDictType(), dictDatas);
+        }
+    }
+
+    /**
+     * 新增保存字典数据信息
+     * 
+     * @param data 字典数据信息
+     * @return 结果
+     */
+    @Override
+    public int insertDictData(SysDictData data)
+    {
+        int row = dictDataMapper.insertDictData(data);
+        if (row > 0)
+        {
+            List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(data.getDictType());
+            DictUtils.setDictCache(data.getDictType(), dictDatas);
+        }
+        return row;
+    }
+
+    /**
+     * 修改保存字典数据信息
+     * 
+     * @param data 字典数据信息
+     * @return 结果
+     */
+    @Override
+    public int updateDictData(SysDictData data)
+    {
+        int row = dictDataMapper.updateDictData(data);
+        if (row > 0)
+        {
+            List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(data.getDictType());
+            DictUtils.setDictCache(data.getDictType(), dictDatas);
+        }
+        return row;
+    }
+
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
new file mode 100644
index 0000000..7fd9654
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
@@ -0,0 +1,223 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.PostConstruct;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.entity.SysDictData;
+import com.ruoyi.common.core.domain.entity.SysDictType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.DictUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.mapper.SysDictDataMapper;
+import com.ruoyi.system.mapper.SysDictTypeMapper;
+import com.ruoyi.system.service.ISysDictTypeService;
+
+/**
+ * 字典 业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysDictTypeServiceImpl implements ISysDictTypeService
+{
+    @Autowired
+    private SysDictTypeMapper dictTypeMapper;
+
+    @Autowired
+    private SysDictDataMapper dictDataMapper;
+
+    /**
+     * 项目启动时,初始化字典到缓存
+     */
+    @PostConstruct
+    public void init()
+    {
+        loadingDictCache();
+    }
+
+    /**
+     * 根据条件分页查询字典类型
+     * 
+     * @param dictType 字典类型信息
+     * @return 字典类型集合信息
+     */
+    @Override
+    public List<SysDictType> selectDictTypeList(SysDictType dictType)
+    {
+        return dictTypeMapper.selectDictTypeList(dictType);
+    }
+
+    /**
+     * 根据所有字典类型
+     * 
+     * @return 字典类型集合信息
+     */
+    @Override
+    public List<SysDictType> selectDictTypeAll()
+    {
+        return dictTypeMapper.selectDictTypeAll();
+    }
+
+    /**
+     * 根据字典类型查询字典数据
+     * 
+     * @param dictType 字典类型
+     * @return 字典数据集合信息
+     */
+    @Override
+    public List<SysDictData> selectDictDataByType(String dictType)
+    {
+        List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
+        if (StringUtils.isNotEmpty(dictDatas))
+        {
+            return dictDatas;
+        }
+        dictDatas = dictDataMapper.selectDictDataByType(dictType);
+        if (StringUtils.isNotEmpty(dictDatas))
+        {
+            DictUtils.setDictCache(dictType, dictDatas);
+            return dictDatas;
+        }
+        return null;
+    }
+
+    /**
+     * 根据字典类型ID查询信息
+     * 
+     * @param dictId 字典类型ID
+     * @return 字典类型
+     */
+    @Override
+    public SysDictType selectDictTypeById(Long dictId)
+    {
+        return dictTypeMapper.selectDictTypeById(dictId);
+    }
+
+    /**
+     * 根据字典类型查询信息
+     * 
+     * @param dictType 字典类型
+     * @return 字典类型
+     */
+    @Override
+    public SysDictType selectDictTypeByType(String dictType)
+    {
+        return dictTypeMapper.selectDictTypeByType(dictType);
+    }
+
+    /**
+     * 批量删除字典类型信息
+     * 
+     * @param dictIds 需要删除的字典ID
+     */
+    @Override
+    public void deleteDictTypeByIds(Long[] dictIds)
+    {
+        for (Long dictId : dictIds)
+        {
+            SysDictType dictType = selectDictTypeById(dictId);
+            if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0)
+            {
+                throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
+            }
+            dictTypeMapper.deleteDictTypeById(dictId);
+            DictUtils.removeDictCache(dictType.getDictType());
+        }
+    }
+
+    /**
+     * 加载字典缓存数据
+     */
+    @Override
+    public void loadingDictCache()
+    {
+        SysDictData dictData = new SysDictData();
+        dictData.setStatus("0");
+        Map<String, List<SysDictData>> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType));
+        for (Map.Entry<String, List<SysDictData>> entry : dictDataMap.entrySet())
+        {
+            DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList()));
+        }
+    }
+
+    /**
+     * 清空字典缓存数据
+     */
+    @Override
+    public void clearDictCache()
+    {
+        DictUtils.clearDictCache();
+    }
+
+    /**
+     * 重置字典缓存数据
+     */
+    @Override
+    public void resetDictCache()
+    {
+        clearDictCache();
+        loadingDictCache();
+    }
+
+    /**
+     * 新增保存字典类型信息
+     * 
+     * @param dict 字典类型信息
+     * @return 结果
+     */
+    @Override
+    public int insertDictType(SysDictType dict)
+    {
+        int row = dictTypeMapper.insertDictType(dict);
+        if (row > 0)
+        {
+            DictUtils.setDictCache(dict.getDictType(), null);
+        }
+        return row;
+    }
+
+    /**
+     * 修改保存字典类型信息
+     * 
+     * @param dict 字典类型信息
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int updateDictType(SysDictType dict)
+    {
+        SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId());
+        dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType());
+        int row = dictTypeMapper.updateDictType(dict);
+        if (row > 0)
+        {
+            List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType());
+            DictUtils.setDictCache(dict.getDictType(), dictDatas);
+        }
+        return row;
+    }
+
+    /**
+     * 校验字典类型称是否唯一
+     * 
+     * @param dict 字典类型
+     * @return 结果
+     */
+    @Override
+    public boolean checkDictTypeUnique(SysDictType dict)
+    {
+        Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId();
+        SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType());
+        if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
new file mode 100644
index 0000000..216aecb
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
@@ -0,0 +1,65 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.domain.SysLogininfor;
+import com.ruoyi.system.mapper.SysLogininforMapper;
+import com.ruoyi.system.service.ISysLogininforService;
+
+/**
+ * 系统访问日志情况信息 服务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysLogininforServiceImpl implements ISysLogininforService
+{
+
+    @Autowired
+    private SysLogininforMapper logininforMapper;
+
+    /**
+     * 新增系统登录日志
+     * 
+     * @param logininfor 访问日志对象
+     */
+    @Override
+    public void insertLogininfor(SysLogininfor logininfor)
+    {
+        logininforMapper.insertLogininfor(logininfor);
+    }
+
+    /**
+     * 查询系统登录日志集合
+     * 
+     * @param logininfor 访问日志对象
+     * @return 登录记录集合
+     */
+    @Override
+    public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor)
+    {
+        return logininforMapper.selectLogininforList(logininfor);
+    }
+
+    /**
+     * 批量删除系统登录日志
+     * 
+     * @param infoIds 需要删除的登录日志ID
+     * @return 结果
+     */
+    @Override
+    public int deleteLogininforByIds(Long[] infoIds)
+    {
+        return logininforMapper.deleteLogininforByIds(infoIds);
+    }
+
+    /**
+     * 清空系统登录日志
+     */
+    @Override
+    public void cleanLogininfor()
+    {
+        logininforMapper.cleanLogininfor();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
new file mode 100644
index 0000000..343c8d7
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -0,0 +1,598 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import com.ruoyi.system.object.vo.MenuTreeVO;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.TreeSelect;
+import com.ruoyi.common.core.domain.entity.SysMenu;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.vo.MetaVo;
+import com.ruoyi.system.domain.vo.RouterVo;
+import com.ruoyi.system.mapper.SysMenuMapper;
+import com.ruoyi.system.mapper.SysRoleMapper;
+import com.ruoyi.system.mapper.SysRoleMenuMapper;
+import com.ruoyi.system.service.ISysMenuService;
+
+/**
+ * 菜单 业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysMenuServiceImpl implements ISysMenuService
+{
+    public static final String PREMISSION_STRING = "perms[\"{0}\"]";
+
+    @Autowired
+    private SysMenuMapper menuMapper;
+
+    @Autowired
+    private SysRoleMapper roleMapper;
+
+    @Autowired
+    private SysRoleMenuMapper roleMenuMapper;
+
+    /**
+     * 根据用户查询系统菜单列表
+     * 
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    @Override
+    public List<SysMenu> selectMenuList(Long userId)
+    {
+        return selectMenuList(new SysMenu(), userId);
+    }
+
+    /**
+     * 查询系统菜单列表
+     * 
+     * @param menu 菜单信息
+     * @return 菜单列表
+     */
+    @Override
+    public List<SysMenu> selectMenuList(SysMenu menu, Long userId)
+    {
+        List<SysMenu> menuList = null;
+        // 管理员显示所有菜单信息
+        if (SysUser.isAdmin(userId))
+        {
+            menuList = menuMapper.selectMenuList(menu);
+        }
+        else
+        {
+            menu.getParams().put("userId", userId);
+            menuList = menuMapper.selectMenuListByUserId(menu);
+        }
+        return menuList;
+    }
+
+    /**
+     * 根据用户ID查询权限
+     * 
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    @Override
+    public Set<String> selectMenuPermsByUserId(Long userId)
+    {
+        List<String> perms = menuMapper.selectMenuPermsByUserId(userId);
+        Set<String> permsSet = new HashSet<>();
+        for (String perm : perms)
+        {
+            if (StringUtils.isNotEmpty(perm))
+            {
+                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
+            }
+        }
+        return permsSet;
+    }
+
+    /**
+     * 根据角色ID查询权限
+     * 
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    @Override
+    public Set<String> selectMenuPermsByRoleId(Long roleId)
+    {
+        List<String> perms = menuMapper.selectMenuPermsByRoleId(roleId);
+        Set<String> permsSet = new HashSet<>();
+        for (String perm : perms)
+        {
+            if (StringUtils.isNotEmpty(perm))
+            {
+                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
+            }
+        }
+        return permsSet;
+    }
+
+    /**
+     * 根据用户ID查询菜单
+     * 
+     * @param userId 用户名称
+     * @return 菜单列表
+     */
+    @Override
+    public List<SysMenu> selectMenuTreeByUserId(Long userId)
+    {
+        List<SysMenu> menus = null;
+        if (SecurityUtils.isAdmin(userId))
+        {
+            menus = menuMapper.selectMenuTreeAll();
+        }
+        else
+        {
+            menus = menuMapper.selectMenuTreeByUserId(userId);
+        }
+        return getChildPerms(menus, 0);
+    }
+
+    /**
+     * 根据角色ID查询菜单树信息
+     * 
+     * @param roleId 角色ID
+     * @return 选中菜单列表
+     */
+    @Override
+    public List<Long> selectMenuListByRoleId(Long roleId)
+    {
+        SysRole role = roleMapper.selectRoleById(roleId);
+        return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
+    }
+
+    /**
+     * 构建前端路由所需要的菜单
+     * 
+     * @param menus 菜单列表
+     * @return 路由列表
+     */
+    @Override
+    public List<RouterVo> buildMenus(List<SysMenu> menus)
+    {
+        List<RouterVo> routers = new LinkedList<RouterVo>();
+        for (SysMenu menu : menus)
+        {
+            RouterVo router = new RouterVo();
+            router.setHidden("1".equals(menu.getVisible()));
+            router.setName(getRouteName(menu));
+            router.setPath(getRouterPath(menu));
+            router.setComponent(getComponent(menu));
+            router.setQuery(menu.getQuery());
+            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
+            List<SysMenu> cMenus = menu.getChildren();
+            if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType()))
+            {
+                router.setAlwaysShow(true);
+                router.setRedirect("noRedirect");
+                router.setChildren(buildMenus(cMenus));
+            }
+            else if (isMenuFrame(menu))
+            {
+                router.setMeta(null);
+                List<RouterVo> childrenList = new ArrayList<RouterVo>();
+                RouterVo children = new RouterVo();
+                children.setPath(menu.getPath());
+                children.setComponent(menu.getComponent());
+                children.setName(getRouteName(menu.getRouteName(), menu.getPath()));
+                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
+                children.setQuery(menu.getQuery());
+                childrenList.add(children);
+                router.setChildren(childrenList);
+            }
+            else if (menu.getParentId().intValue() == 0 && isInnerLink(menu))
+            {
+                router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
+                router.setPath("/");
+                List<RouterVo> childrenList = new ArrayList<RouterVo>();
+                RouterVo children = new RouterVo();
+                String routerPath = innerLinkReplaceEach(menu.getPath());
+                children.setPath(routerPath);
+                children.setComponent(UserConstants.INNER_LINK);
+                children.setName(getRouteName(menu.getRouteName(), routerPath));
+                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
+                childrenList.add(children);
+                router.setChildren(childrenList);
+            }
+            routers.add(router);
+        }
+        return routers;
+    }
+
+    /**
+     * 构建前端所需要树结构
+     * 
+     * @param menus 菜单列表
+     * @return 树结构列表
+     */
+    @Override
+    public List<SysMenu> buildMenuTree(List<SysMenu> menus)
+    {
+        List<SysMenu> returnList = new ArrayList<SysMenu>();
+        List<Long> tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList());
+        for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext();)
+        {
+            SysMenu menu = (SysMenu) iterator.next();
+            // 如果是顶级节点, 遍历该父节点的所有子节点
+            if (!tempList.contains(menu.getParentId()))
+            {
+                recursionFn(menus, menu);
+                returnList.add(menu);
+            }
+        }
+        if (returnList.isEmpty())
+        {
+            returnList = menus;
+        }
+        return returnList;
+    }
+
+    /**
+     * 构建前端所需要下拉树结构
+     * 
+     * @param menus 菜单列表
+     * @return 下拉树结构列表
+     */
+    @Override
+    public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus)
+    {
+        List<SysMenu> menuTrees = buildMenuTree(menus);
+        return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
+    }
+
+    /**
+     * 根据菜单ID查询信息
+     * 
+     * @param menuId 菜单ID
+     * @return 菜单信息
+     */
+    @Override
+    public SysMenu selectMenuById(Long menuId)
+    {
+        return menuMapper.selectMenuById(menuId);
+    }
+
+    /**
+     * 是否存在菜单子节点
+     * 
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    @Override
+    public boolean hasChildByMenuId(Long menuId)
+    {
+        int result = menuMapper.hasChildByMenuId(menuId);
+        return result > 0;
+    }
+
+    /**
+     * 查询菜单使用数量
+     * 
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    @Override
+    public boolean checkMenuExistRole(Long menuId)
+    {
+        int result = roleMenuMapper.checkMenuExistRole(menuId);
+        return result > 0;
+    }
+
+    /**
+     * 新增保存菜单信息
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    @Override
+    public int insertMenu(SysMenu menu)
+    {
+        return menuMapper.insertMenu(menu);
+    }
+
+    /**
+     * 修改保存菜单信息
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    @Override
+    public int updateMenu(SysMenu menu)
+    {
+        return menuMapper.updateMenu(menu);
+    }
+
+    /**
+     * 删除菜单管理信息
+     * 
+     * @param menuId 菜单ID
+     * @return 结果
+     */
+    @Override
+    public int deleteMenuById(Long menuId)
+    {
+        return menuMapper.deleteMenuById(menuId);
+    }
+
+    /**
+     * 校验菜单名称是否唯一
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkMenuNameUnique(SysMenu menu)
+    {
+        Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
+        SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
+        if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    @Override
+    public List<MenuTreeVO> tree(Long userId) {
+        //  构建菜单树
+        return buildMenuTree(userId);
+    }
+    /**
+     * 构建菜单树(递归实现)
+     * @return 菜单树列表
+     */
+    public List<MenuTreeVO> buildMenuTree(Long userId) {
+        // 1. 根据用户ID查询角色ID集合
+        List<Long> roleList = roleMapper.selectRoleListByUserId(userId);
+        // 2. 查询角色关联的菜单ID
+        List<Long> menuIds = roleMenuMapper.selectMenuIdsByRoleIds(roleList);
+        // 获取所有菜单
+        List<MenuTreeVO> rootMenus = menuMapper.getAllRootMenu();
+        // 递归构建子菜单
+        List<MenuTreeVO> list = rootMenus.stream()
+                .map(x -> convertToMenuTreeVO(x,menuIds))
+                .collect(Collectors.toList());
+        return list.stream().filter(this::shouldIncludeMenu).collect(Collectors.toList());
+
+    }
+    /**
+     * 递归转换菜单并构建子菜单树
+     */
+    private MenuTreeVO convertToMenuTreeVO(MenuTreeVO menu,List<Long> menuIds) {
+        MenuTreeVO vo = new MenuTreeVO();
+        BeanUtils.copyProperties(menu, vo, "children"); // 复制基本属性,忽略children字段
+
+        // 获取当前菜单的子菜单
+        List<MenuTreeVO> childMenus = menuMapper.selectMenusByParentId(menu.getMenuId());
+
+        // 递归处理子菜单
+        if (!childMenus.isEmpty()) {
+            childMenus = childMenus.stream()
+                    .filter(x -> !("C".equals(x.getMenuType()) && !menuIds.contains(x.getMenuId())))
+                    .collect(Collectors.toList());
+
+            List<MenuTreeVO> filteredChildren = childMenus.stream()
+                    .map(x -> convertToMenuTreeVO(x,menuIds))
+                    .filter(this::shouldIncludeMenu) // 过滤子菜单
+                    .collect(Collectors.toList());
+
+            vo.setChildren(filteredChildren);
+        }
+
+        return vo;
+    }
+    /**
+     * 判断菜单是否应该被包含在结果中
+     * - 如果是M类型且没有子菜单,则不包含
+     * - 否则包含
+     */
+    private boolean shouldIncludeMenu(MenuTreeVO menu) {
+        // 如果菜单类型是M(目录)且没有子菜单,则过滤掉
+        return !("M".equals(menu.getMenuType()) && (menu.getChildren() == null || menu.getChildren().isEmpty()));
+    }
+    /**
+     * 获取路由名称
+     * 
+     * @param menu 菜单信息
+     * @return 路由名称
+     */
+    public String getRouteName(SysMenu menu)
+    {
+        // 非外链并且是一级目录(类型为目录)
+        if (isMenuFrame(menu))
+        {
+            return StringUtils.EMPTY;
+        }
+        return getRouteName(menu.getRouteName(), menu.getPath());
+    }
+
+    /**
+     * 获取路由名称,如没有配置路由名称则取路由地址
+     * 
+     * @param name 路由名称
+     * @param path 路由地址
+     * @return 路由名称(驼峰格式)
+     */
+    public String getRouteName(String name, String path)
+    {
+        String routerName = StringUtils.isNotEmpty(name) ? name : path;
+        return StringUtils.capitalize(routerName);
+    }
+
+    /**
+     * 获取路由地址
+     * 
+     * @param menu 菜单信息
+     * @return 路由地址
+     */
+    public String getRouterPath(SysMenu menu)
+    {
+        String routerPath = menu.getPath();
+        // 内链打开外网方式
+        if (menu.getParentId().intValue() != 0 && isInnerLink(menu))
+        {
+            routerPath = innerLinkReplaceEach(routerPath);
+        }
+        // 非外链并且是一级目录(类型为目录)
+        if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
+                && UserConstants.NO_FRAME.equals(menu.getIsFrame()))
+        {
+            routerPath = "/" + menu.getPath();
+        }
+        // 非外链并且是一级目录(类型为菜单)
+        else if (isMenuFrame(menu))
+        {
+            routerPath = "/";
+        }
+        return routerPath;
+    }
+
+    /**
+     * 获取组件信息
+     * 
+     * @param menu 菜单信息
+     * @return 组件信息
+     */
+    public String getComponent(SysMenu menu)
+    {
+        String component = UserConstants.LAYOUT;
+        if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu))
+        {
+            component = menu.getComponent();
+        }
+        else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu))
+        {
+            component = UserConstants.INNER_LINK;
+        }
+        else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu))
+        {
+            component = UserConstants.PARENT_VIEW;
+        }
+        return component;
+    }
+
+    /**
+     * 是否为菜单内部跳转
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public boolean isMenuFrame(SysMenu menu)
+    {
+        return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())
+                && menu.getIsFrame().equals(UserConstants.NO_FRAME);
+    }
+
+    /**
+     * 是否为内链组件
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public boolean isInnerLink(SysMenu menu)
+    {
+        return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath());
+    }
+
+    /**
+     * 是否为parent_view组件
+     * 
+     * @param menu 菜单信息
+     * @return 结果
+     */
+    public boolean isParentView(SysMenu menu)
+    {
+        return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());
+    }
+
+    /**
+     * 根据父节点的ID获取所有子节点
+     * 
+     * @param list 分类表
+     * @param parentId 传入的父节点ID
+     * @return String
+     */
+    public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId)
+    {
+        List<SysMenu> returnList = new ArrayList<SysMenu>();
+        for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();)
+        {
+            SysMenu t = (SysMenu) iterator.next();
+            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
+            if (t.getParentId() == parentId)
+            {
+                recursionFn(list, t);
+                returnList.add(t);
+            }
+        }
+        return returnList;
+    }
+
+    /**
+     * 递归列表
+     * 
+     * @param list 分类表
+     * @param t 子节点
+     */
+    private void recursionFn(List<SysMenu> list, SysMenu t)
+    {
+        // 得到子节点列表
+        List<SysMenu> childList = getChildList(list, t);
+        t.setChildren(childList);
+        for (SysMenu tChild : childList)
+        {
+            if (hasChild(list, tChild))
+            {
+                recursionFn(list, tChild);
+            }
+        }
+    }
+
+    /**
+     * 得到子节点列表
+     */
+    private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t)
+    {
+        List<SysMenu> tlist = new ArrayList<SysMenu>();
+        Iterator<SysMenu> it = list.iterator();
+        while (it.hasNext())
+        {
+            SysMenu n = (SysMenu) it.next();
+            if (n.getParentId().longValue() == t.getMenuId().longValue())
+            {
+                tlist.add(n);
+            }
+        }
+        return tlist;
+    }
+
+    /**
+     * 判断是否有子节点
+     */
+    private boolean hasChild(List<SysMenu> list, SysMenu t)
+    {
+        return getChildList(list, t).size() > 0;
+    }
+
+    /**
+     * 内链域名特殊字符替换
+     * 
+     * @return 替换后的内链域名
+     */
+    public String innerLinkReplaceEach(String path)
+    {
+        return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":" },
+                new String[] { "", "", "", "/", "/" });
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java
new file mode 100644
index 0000000..14688e1
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java
@@ -0,0 +1,108 @@
+package com.ruoyi.system.service.impl;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.domain.SysNotice;
+import com.ruoyi.system.mapper.SysNoticeMapper;
+import com.ruoyi.system.service.ISysNoticeService;
+
+/**
+ * 公告 服务层实现
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysNoticeServiceImpl extends ServiceImpl<SysNoticeMapper,SysNotice> implements ISysNoticeService {
+    @Autowired
+    private SysNoticeMapper noticeMapper;
+
+    /**
+     * 查询公告信息
+     *
+     * @param noticeId 公告ID
+     * @return 公告信息
+     */
+    @Override
+    public SysNotice selectNoticeById(Long noticeId) {
+        return noticeMapper.selectNoticeById(noticeId);
+    }
+
+    /**
+     * 查询公告列表
+     *
+     * @param notice 公告信息
+     * @return 公告集合
+     */
+    @Override
+    public List<SysNotice> selectNoticeList(SysNotice notice) {
+        return noticeMapper.selectNoticeList(notice);
+    }
+
+    /**
+     * 新增公告
+     *
+     * @param notice 公告信息
+     * @return 结果
+     */
+    @Override
+    public int insertNotice(SysNotice notice) {
+        return noticeMapper.insertNotice(notice);
+    }
+
+    /**
+     * 修改公告
+     *
+     * @param notice 公告信息
+     * @return 结果
+     */
+    @Override
+    public int updateNotice(SysNotice notice) {
+        return noticeMapper.updateNotice(notice);
+    }
+
+    /**
+     * 删除公告对象
+     *
+     * @param noticeId 公告ID
+     * @return 结果
+     */
+    @Override
+    public int deleteNoticeById(Long noticeId) {
+        return noticeMapper.deleteNoticeById(noticeId);
+    }
+
+    /**
+     * 批量删除公告信息
+     *
+     * @param noticeIds 需要删除的公告ID
+     * @return 结果
+     */
+    @Override
+    public int deleteNoticeByIds(Long[] noticeIds) {
+        return noticeMapper.deleteNoticeByIds(noticeIds);
+    }
+
+    @Override
+    public List<SysNotice> alwaysMessage() {
+        LambdaQueryWrapper<SysNotice> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(SysNotice::getNoticeId, 1, 2, 3,4);
+        queryWrapper.gt(SysNotice::getCountNum, 0);
+        return this.baseMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public List<SysNotice> payMessage() {
+        LambdaQueryWrapper<SysNotice> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(SysNotice::getNoticeId, 5);
+        queryWrapper.gt(SysNotice::getCountNum, 0);
+        queryWrapper.orderByDesc(SysNotice::getNoticeId);
+        return this.baseMapper.selectList(queryWrapper);
+    }
+
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
new file mode 100644
index 0000000..5489815
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
@@ -0,0 +1,76 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.domain.SysOperLog;
+import com.ruoyi.system.mapper.SysOperLogMapper;
+import com.ruoyi.system.service.ISysOperLogService;
+
+/**
+ * 操作日志 服务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysOperLogServiceImpl implements ISysOperLogService
+{
+    @Autowired
+    private SysOperLogMapper operLogMapper;
+
+    /**
+     * 新增操作日志
+     * 
+     * @param operLog 操作日志对象
+     */
+    @Override
+    public void insertOperlog(SysOperLog operLog)
+    {
+        operLogMapper.insertOperlog(operLog);
+    }
+
+    /**
+     * 查询系统操作日志集合
+     * 
+     * @param operLog 操作日志对象
+     * @return 操作日志集合
+     */
+    @Override
+    public List<SysOperLog> selectOperLogList(SysOperLog operLog)
+    {
+        return operLogMapper.selectOperLogList(operLog);
+    }
+
+    /**
+     * 批量删除系统操作日志
+     * 
+     * @param operIds 需要删除的操作日志ID
+     * @return 结果
+     */
+    @Override
+    public int deleteOperLogByIds(Long[] operIds)
+    {
+        return operLogMapper.deleteOperLogByIds(operIds);
+    }
+
+    /**
+     * 查询操作日志详细
+     * 
+     * @param operId 操作ID
+     * @return 操作日志对象
+     */
+    @Override
+    public SysOperLog selectOperLogById(Long operId)
+    {
+        return operLogMapper.selectOperLogById(operId);
+    }
+
+    /**
+     * 清空操作日志
+     */
+    @Override
+    public void cleanOperLog()
+    {
+        operLogMapper.cleanOperLog();
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
new file mode 100644
index 0000000..5e5fe06
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
@@ -0,0 +1,178 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.SysPost;
+import com.ruoyi.system.mapper.SysPostMapper;
+import com.ruoyi.system.mapper.SysUserPostMapper;
+import com.ruoyi.system.service.ISysPostService;
+
+/**
+ * 岗位信息 服务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysPostServiceImpl implements ISysPostService
+{
+    @Autowired
+    private SysPostMapper postMapper;
+
+    @Autowired
+    private SysUserPostMapper userPostMapper;
+
+    /**
+     * 查询岗位信息集合
+     * 
+     * @param post 岗位信息
+     * @return 岗位信息集合
+     */
+    @Override
+    public List<SysPost> selectPostList(SysPost post)
+    {
+        return postMapper.selectPostList(post);
+    }
+
+    /**
+     * 查询所有岗位
+     * 
+     * @return 岗位列表
+     */
+    @Override
+    public List<SysPost> selectPostAll()
+    {
+        return postMapper.selectPostAll();
+    }
+
+    /**
+     * 通过岗位ID查询岗位信息
+     * 
+     * @param postId 岗位ID
+     * @return 角色对象信息
+     */
+    @Override
+    public SysPost selectPostById(Long postId)
+    {
+        return postMapper.selectPostById(postId);
+    }
+
+    /**
+     * 根据用户ID获取岗位选择框列表
+     * 
+     * @param userId 用户ID
+     * @return 选中岗位ID列表
+     */
+    @Override
+    public List<Long> selectPostListByUserId(Long userId)
+    {
+        return postMapper.selectPostListByUserId(userId);
+    }
+
+    /**
+     * 校验岗位名称是否唯一
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkPostNameUnique(SysPost post)
+    {
+        Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId();
+        SysPost info = postMapper.checkPostNameUnique(post.getPostName());
+        if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验岗位编码是否唯一
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkPostCodeUnique(SysPost post)
+    {
+        Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId();
+        SysPost info = postMapper.checkPostCodeUnique(post.getPostCode());
+        if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 通过岗位ID查询岗位使用数量
+     * 
+     * @param postId 岗位ID
+     * @return 结果
+     */
+    @Override
+    public int countUserPostById(Long postId)
+    {
+        return userPostMapper.countUserPostById(postId);
+    }
+
+    /**
+     * 删除岗位信息
+     * 
+     * @param postId 岗位ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostById(Long postId)
+    {
+        return postMapper.deletePostById(postId);
+    }
+
+    /**
+     * 批量删除岗位信息
+     * 
+     * @param postIds 需要删除的岗位ID
+     * @return 结果
+     */
+    @Override
+    public int deletePostByIds(Long[] postIds)
+    {
+        for (Long postId : postIds)
+        {
+            SysPost post = selectPostById(postId);
+            if (countUserPostById(postId) > 0)
+            {
+                throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName()));
+            }
+        }
+        return postMapper.deletePostByIds(postIds);
+    }
+
+    /**
+     * 新增保存岗位信息
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    @Override
+    public int insertPost(SysPost post)
+    {
+        return postMapper.insertPost(post);
+    }
+
+    /**
+     * 修改保存岗位信息
+     * 
+     * @param post 岗位信息
+     * @return 结果
+     */
+    @Override
+    public int updatePost(SysPost post)
+    {
+        return postMapper.updatePost(post);
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
new file mode 100644
index 0000000..3d5b753
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
@@ -0,0 +1,498 @@
+package com.ruoyi.system.service.impl;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.system.object.dto.AddSysRoleDTO;
+import com.ruoyi.system.object.dto.EditSysRoleDTO;
+import com.ruoyi.system.object.vo.SysRolePageVO;
+import com.ruoyi.system.object.vo.SysRoleVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.system.domain.SysRoleDept;
+import com.ruoyi.system.domain.SysRoleMenu;
+import com.ruoyi.system.domain.SysUserRole;
+import com.ruoyi.system.mapper.SysRoleDeptMapper;
+import com.ruoyi.system.mapper.SysRoleMapper;
+import com.ruoyi.system.mapper.SysRoleMenuMapper;
+import com.ruoyi.system.mapper.SysUserRoleMapper;
+import com.ruoyi.system.service.ISysRoleService;
+
+/**
+ * 角色 业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysRoleServiceImpl implements ISysRoleService
+{
+    @Autowired
+    private SysRoleMapper roleMapper;
+
+    @Autowired
+    private SysRoleMenuMapper roleMenuMapper;
+
+    @Autowired
+    private SysUserRoleMapper userRoleMapper;
+
+    @Autowired
+    private SysRoleDeptMapper roleDeptMapper;
+
+    /**
+     * 根据条件分页查询角色数据
+     * 
+     * @param role 角色信息
+     * @return 角色数据集合信息
+     */
+    @Override
+    @DataScope(deptAlias = "d")
+    public List<SysRole> selectRoleList(SysRole role)
+    {
+        return roleMapper.selectRoleList(role);
+    }
+
+    /**
+     * 根据用户ID查询角色
+     * 
+     * @param userId 用户ID
+     * @return 角色列表
+     */
+    @Override
+    public List<SysRole> selectRolesByUserId(Long userId)
+    {
+        List<SysRole> userRoles = roleMapper.selectRolePermissionByUserId(userId);
+        List<SysRole> roles = selectRoleAll();
+        for (SysRole role : roles)
+        {
+            for (SysRole userRole : userRoles)
+            {
+                if (role.getRoleId().longValue() == userRole.getRoleId().longValue())
+                {
+                    role.setFlag(true);
+                    break;
+                }
+            }
+        }
+        return roles;
+    }
+
+    /**
+     * 根据用户ID查询权限
+     * 
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    @Override
+    public Set<String> selectRolePermissionByUserId(Long userId)
+    {
+        List<SysRole> perms = roleMapper.selectRolePermissionByUserId(userId);
+        Set<String> permsSet = new HashSet<>();
+        for (SysRole perm : perms)
+        {
+            if (StringUtils.isNotNull(perm))
+            {
+                permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(",")));
+            }
+        }
+        return permsSet;
+    }
+
+    /**
+     * 查询所有角色
+     * 
+     * @return 角色列表
+     */
+    @Override
+    public List<SysRole> selectRoleAll()
+    {
+        return SpringUtils.getAopProxy(this).selectRoleList(new SysRole());
+    }
+
+    /**
+     * 根据用户ID获取角色选择框列表
+     * 
+     * @param userId 用户ID
+     * @return 选中角色ID列表
+     */
+    @Override
+    public List<Long> selectRoleListByUserId(Long userId)
+    {
+        return roleMapper.selectRoleListByUserId(userId);
+    }
+
+    /**
+     * 通过角色ID查询角色
+     * 
+     * @param roleId 角色ID
+     * @return 角色对象信息
+     */
+    @Override
+    public SysRole selectRoleById(Long roleId)
+    {
+        return roleMapper.selectRoleById(roleId);
+    }
+
+    /**
+     * 校验角色名称是否唯一
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkRoleNameUnique(SysRole role)
+    {
+        Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId();
+        SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName());
+        if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验角色权限是否唯一
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkRoleKeyUnique(SysRole role)
+    {
+        Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId();
+        SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey());
+        if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验角色是否允许操作
+     * 
+     * @param role 角色信息
+     */
+    @Override
+    public void checkRoleAllowed(SysRole role)
+    {
+        if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin())
+        {
+            throw new ServiceException("不允许操作超级管理员角色");
+        }
+    }
+
+    /**
+     * 校验角色是否有数据权限
+     * 
+     * @param roleIds 角色id
+     */
+    @Override
+    public void checkRoleDataScope(Long... roleIds)
+    {
+        if (!SysUser.isAdmin(SecurityUtils.getUserId()))
+        {
+            for (Long roleId : roleIds)
+            {
+                SysRole role = new SysRole();
+                role.setRoleId(roleId);
+                List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
+                if (StringUtils.isEmpty(roles))
+                {
+                    throw new ServiceException("没有权限访问角色数据!");
+                }
+            }
+        }
+    }
+
+    /**
+     * 通过角色ID查询角色使用数量
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    @Override
+    public int countUserRoleByRoleId(Long roleId)
+    {
+        return userRoleMapper.countUserRoleByRoleId(roleId);
+    }
+
+    /**
+     * 新增保存角色信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int insertRole(SysRole role)
+    {
+        // 新增角色信息
+        roleMapper.insertRole(role);
+        return insertRoleMenu(role);
+    }
+
+    /**
+     * 修改保存角色信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int updateRole(SysRole role)
+    {
+        // 修改角色信息
+        roleMapper.updateRole(role);
+        // 删除角色与菜单关联
+        roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId());
+        return insertRoleMenu(role);
+    }
+
+    /**
+     * 修改角色状态
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    @Override
+    public int updateRoleStatus(SysRole role)
+    {
+        return roleMapper.updateRole(role);
+    }
+
+    /**
+     * 修改数据权限信息
+     * 
+     * @param role 角色信息
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int authDataScope(SysRole role)
+    {
+        // 修改角色信息
+        roleMapper.updateRole(role);
+        // 删除角色与部门关联
+        roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId());
+        // 新增角色和部门信息(数据权限)
+        return insertRoleDept(role);
+    }
+
+    /**
+     * 新增角色菜单信息
+     * 
+     * @param role 角色对象
+     */
+    public int insertRoleMenu(SysRole role)
+    {
+        int rows = 1;
+        // 新增用户与角色管理
+        List<SysRoleMenu> list = new ArrayList<SysRoleMenu>();
+        for (Long menuId : role.getMenuIds())
+        {
+            SysRoleMenu rm = new SysRoleMenu();
+            rm.setRoleId(role.getRoleId());
+            rm.setMenuId(menuId);
+            list.add(rm);
+        }
+        if (list.size() > 0)
+        {
+            rows = roleMenuMapper.batchRoleMenu(list);
+        }
+        return rows;
+    }
+
+    /**
+     * 新增角色部门信息(数据权限)
+     *
+     * @param role 角色对象
+     */
+    public int insertRoleDept(SysRole role)
+    {
+        int rows = 1;
+        // 新增角色与部门(数据权限)管理
+        List<SysRoleDept> list = new ArrayList<SysRoleDept>();
+        for (Long deptId : role.getDeptIds())
+        {
+            SysRoleDept rd = new SysRoleDept();
+            rd.setRoleId(role.getRoleId());
+            rd.setDeptId(deptId);
+            list.add(rd);
+        }
+        if (list.size() > 0)
+        {
+            rows = roleDeptMapper.batchRoleDept(list);
+        }
+        return rows;
+    }
+
+    /**
+     * 通过角色ID删除角色
+     * 
+     * @param roleId 角色ID
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int deleteRoleById(Long roleId)
+    {
+        // 删除角色与菜单关联
+        roleMenuMapper.deleteRoleMenuByRoleId(roleId);
+        // 删除角色与部门关联
+//        roleDeptMapper.deleteRoleDeptByRoleId(roleId);
+        return roleMapper.deleteRoleById(roleId);
+    }
+
+    /**
+     * 批量删除角色信息
+     * 
+     * @param roleIds 需要删除的角色ID
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int deleteRoleByIds(Long[] roleIds)
+    {
+        for (Long roleId : roleIds)
+        {
+            checkRoleAllowed(new SysRole(roleId));
+            checkRoleDataScope(roleId);
+            SysRole role = selectRoleById(roleId);
+            if (countUserRoleByRoleId(roleId) > 0)
+            {
+                throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName()));
+            }
+        }
+        // 删除角色与菜单关联
+        roleMenuMapper.deleteRoleMenu(roleIds);
+        // 删除角色与部门关联
+        roleDeptMapper.deleteRoleDept(roleIds);
+        return roleMapper.deleteRoleByIds(roleIds);
+    }
+
+    /**
+     * 取消授权用户角色
+     * 
+     * @param userRole 用户和角色关联信息
+     * @return 结果
+     */
+    @Override
+    public int deleteAuthUser(SysUserRole userRole)
+    {
+        return userRoleMapper.deleteUserRoleInfo(userRole);
+    }
+
+    /**
+     * 批量取消授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要取消授权的用户数据ID
+     * @return 结果
+     */
+    @Override
+    public int deleteAuthUsers(Long roleId, Long[] userIds)
+    {
+        return userRoleMapper.deleteUserRoleInfos(roleId, userIds);
+    }
+
+    /**
+     * 批量选择授权用户角色
+     * 
+     * @param roleId 角色ID
+     * @param userIds 需要授权的用户数据ID
+     * @return 结果
+     */
+    @Override
+    public int insertAuthUsers(Long roleId, Long[] userIds)
+    {
+        // 新增用户与角色管理
+        List<SysUserRole> list = new ArrayList<SysUserRole>();
+        for (Long userId : userIds)
+        {
+            SysUserRole ur = new SysUserRole();
+            ur.setUserId(userId);
+            ur.setRoleId(roleId);
+            list.add(ur);
+        }
+        return userRoleMapper.batchUserRole(list);
+    }
+
+    @Override
+    public IPage<SysRolePageVO> page(IPage<SysRolePageVO> page, String name) {
+        return roleMapper.page(page,name);
+    }
+
+    @Override
+    @Transactional
+    public void add(AddSysRoleDTO dto) {
+        //判断角色名称是否存在
+        SysRole sysRole = new SysRole();
+        sysRole.setRoleName(dto.getRoleName());
+        List<SysRole> sysRoles = roleMapper.selectRoleList(sysRole);
+        if (sysRoles!=null && !sysRoles.isEmpty()){
+            throw new ServiceException("该角色名称重复");
+        }
+        //添加角色
+        sysRole.setStatus("0");
+        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        sysRole.setCreateBy(loginUser.getUsername());
+        sysRole.setCreateTime(DateUtils.getNowDate());
+        roleMapper.insertRole(sysRole);
+        //添加角色菜单
+        roleMenuMapper.insert(sysRole.getRoleId(),dto.getMenuIds());
+    }
+
+    @Override
+    public void edit(EditSysRoleDTO dto) {
+        //判断角色id是否存在
+        SysRole sysRole1 = roleMapper.selectRoleById(dto.getRoleId());
+        if (sysRole1==null||sysRole1.getDelFlag().equals("1")){
+            throw new ServiceException("该角色不存在");
+        }
+        //判断角色名称是否存在
+        SysRole sysRole = new SysRole();
+        sysRole.setRoleName(dto.getRoleName());
+        List<SysRole> sysRoles = roleMapper.selectRoleList(sysRole);
+        if (sysRoles!=null && !sysRoles.isEmpty()){
+            throw new ServiceException("该角色名称重复");
+        }
+        //删除之前的
+        roleMenuMapper.deleteRoleMenuByRoleId(dto.getRoleId());
+        //添加角色菜单
+        roleMenuMapper.insert(sysRole.getRoleId(),dto.getMenuIds());
+    }
+
+    @Override
+    public SysRoleVO getById(Long roleId) {
+        SysRoleVO sysRoleVO = new SysRoleVO();
+        SysRole sysRole = roleMapper.selectRoleById(roleId);
+        if (sysRole==null||sysRole.getDelFlag().equals("1")){
+            throw new ServiceException("该角色不存在");
+        }
+        sysRoleVO.setRoleName(sysRole.getRoleName());
+        sysRoleVO.setRoleId(roleId);
+        ArrayList<Long> roleIds = new ArrayList<>();
+        roleIds.add(roleId);
+        List<Long> menuIds = roleMenuMapper.selectMenuIdsByRoleIds(roleIds);
+        sysRoleVO.setMenuIdList(menuIds);
+        return sysRoleVO;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java
new file mode 100644
index 0000000..f80a877
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java
@@ -0,0 +1,96 @@
+package com.ruoyi.system.service.impl;
+
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.SysUserOnline;
+import com.ruoyi.system.service.ISysUserOnlineService;
+
+/**
+ * 在线用户 服务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysUserOnlineServiceImpl implements ISysUserOnlineService
+{
+    /**
+     * 通过登录地址查询信息
+     * 
+     * @param ipaddr 登录地址
+     * @param user 用户信息
+     * @return 在线用户信息
+     */
+    @Override
+    public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user)
+    {
+        if (StringUtils.equals(ipaddr, user.getIpaddr()))
+        {
+            return loginUserToUserOnline(user);
+        }
+        return null;
+    }
+
+    /**
+     * 通过用户名称查询信息
+     * 
+     * @param userName 用户名称
+     * @param user 用户信息
+     * @return 在线用户信息
+     */
+    @Override
+    public SysUserOnline selectOnlineByUserName(String userName, LoginUser user)
+    {
+        if (StringUtils.equals(userName, user.getUsername()))
+        {
+            return loginUserToUserOnline(user);
+        }
+        return null;
+    }
+
+    /**
+     * 通过登录地址/用户名称查询信息
+     * 
+     * @param ipaddr 登录地址
+     * @param userName 用户名称
+     * @param user 用户信息
+     * @return 在线用户信息
+     */
+    @Override
+    public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user)
+    {
+        if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
+        {
+            return loginUserToUserOnline(user);
+        }
+        return null;
+    }
+
+    /**
+     * 设置在线用户信息
+     * 
+     * @param user 用户信息
+     * @return 在线用户
+     */
+    @Override
+    public SysUserOnline loginUserToUserOnline(LoginUser user)
+    {
+        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser()))
+        {
+            return null;
+        }
+        SysUserOnline sysUserOnline = new SysUserOnline();
+        sysUserOnline.setTokenId(user.getToken());
+        sysUserOnline.setUserName(user.getUsername());
+        sysUserOnline.setIpaddr(user.getIpaddr());
+        sysUserOnline.setLoginLocation(user.getLoginLocation());
+        sysUserOnline.setBrowser(user.getBrowser());
+        sysUserOnline.setOs(user.getOs());
+        sysUserOnline.setLoginTime(user.getLoginTime());
+        if (StringUtils.isNotNull(user.getUser().getDept()))
+        {
+            sysUserOnline.setDeptName(user.getUser().getDept().getDeptName());
+        }
+        return sysUserOnline;
+    }
+}
diff --git a/pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
new file mode 100644
index 0000000..c19ae56
--- /dev/null
+++ b/pt-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -0,0 +1,682 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import javax.validation.Validator;
+
+import cn.hutool.core.util.ObjUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.common.utils.sign.Md5Utils;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import com.ruoyi.common.annotation.DataScope;
+import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.entity.SysRole;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.bean.BeanValidators;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.system.domain.SysPost;
+import com.ruoyi.system.domain.SysUserPost;
+import com.ruoyi.system.domain.SysUserRole;
+import com.ruoyi.system.mapper.SysPostMapper;
+import com.ruoyi.system.mapper.SysRoleMapper;
+import com.ruoyi.system.mapper.SysUserMapper;
+import com.ruoyi.system.mapper.SysUserPostMapper;
+import com.ruoyi.system.mapper.SysUserRoleMapper;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysDeptService;
+import com.ruoyi.system.service.ISysUserService;
+
+/**
+ * 用户 业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class SysUserServiceImpl implements ISysUserService
+{
+    private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
+
+    @Autowired
+    private SysUserMapper userMapper;
+
+    @Autowired
+    private SysRoleMapper roleMapper;
+
+    @Autowired
+    private SysPostMapper postMapper;
+
+    @Autowired
+    private SysUserRoleMapper userRoleMapper;
+
+    @Autowired
+    private SysUserPostMapper userPostMapper;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private ISysDeptService deptService;
+
+    @Autowired
+    protected Validator validator;
+
+    /**
+     * 根据条件分页查询用户列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    @Override
+    @DataScope(deptAlias = "d", userAlias = "u")
+    public List<SysUser> selectUserList(SysUser user)
+    {
+        List<SysUser> sysUsers = userMapper.selectUserList(user);
+        if(ObjUtil.isNotEmpty(sysUsers)){
+            for(SysUser sysUser : sysUsers){
+                List<SysRole> roles = roleMapper.selectRolesByUserName(sysUser.getUserName());
+
+                if(ObjUtil.isEmpty(roles)){
+                    continue;
+                }
+                sysUser.setRoles(roles);
+            }
+        }
+        return sysUsers;
+    }
+
+    /**
+     * 根据条件分页查询已分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    @Override
+    @DataScope(deptAlias = "d", userAlias = "u")
+    public List<SysUser> selectAllocatedList(SysUser user)
+    {
+        return userMapper.selectAllocatedList(user);
+    }
+
+    /**
+     * 根据条件分页查询未分配用户角色列表
+     * 
+     * @param user 用户信息
+     * @return 用户信息集合信息
+     */
+    @Override
+    @DataScope(deptAlias = "d", userAlias = "u")
+    public List<SysUser> selectUnallocatedList(SysUser user)
+    {
+        return userMapper.selectUnallocatedList(user);
+    }
+
+    /**
+     * 通过用户名查询用户
+     * 
+     * @param userName 用户名
+     * @return 用户对象信息
+     */
+    @Override
+    public SysUser selectUserByUserName(String userName)
+    {
+        return userMapper.selectUserByUserName(userName);
+    }
+
+    /**
+     * 通过用户ID查询用户
+     * 
+     * @param userId 用户ID
+     * @return 用户对象信息
+     */
+    @Override
+    public SysUser selectUserById(Long userId)
+    {
+        return userMapper.selectUserById(userId);
+    }
+
+    /**
+     * 查询用户所属角色组
+     * 
+     * @param userName 用户名
+     * @return 结果
+     */
+    @Override
+    public String selectUserRoleGroup(String userName)
+    {
+        List<SysRole> list = roleMapper.selectRolesByUserName(userName);
+        if (CollectionUtils.isEmpty(list))
+        {
+            return StringUtils.EMPTY;
+        }
+        return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(","));
+    }
+
+    /**
+     * 查询用户所属岗位组
+     * 
+     * @param userName 用户名
+     * @return 结果
+     */
+    @Override
+    public String selectUserPostGroup(String userName)
+    {
+        List<SysPost> list = postMapper.selectPostsByUserName(userName);
+        if (CollectionUtils.isEmpty(list))
+        {
+            return StringUtils.EMPTY;
+        }
+        return list.stream().map(SysPost::getPostName).collect(Collectors.joining(","));
+    }
+
+    /**
+     * 校验用户名称是否唯一
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    @Override
+    public boolean checkUserNameUnique(SysUser user)
+    {
+        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
+        SysUser info = userMapper.checkUserNameUnique(user.getUserName());
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验手机号码是否唯一
+     *
+     * @param user 用户信息
+     * @return
+     */
+    @Override
+    public boolean checkPhoneUnique(SysUser user)
+    {
+        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
+        SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber());
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验email是否唯一
+     *
+     * @param user 用户信息
+     * @return
+     */
+    @Override
+    public boolean checkEmailUnique(SysUser user)
+    {
+        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
+        SysUser info = userMapper.checkEmailUnique(user.getEmail());
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
+        {
+            return UserConstants.NOT_UNIQUE;
+        }
+        return UserConstants.UNIQUE;
+    }
+
+    /**
+     * 校验用户是否允许操作
+     * 
+     * @param user 用户信息
+     */
+    @Override
+    public void checkUserAllowed(SysUser user)
+    {
+        if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin())
+        {
+            throw new ServiceException("不允许操作超级管理员用户");
+        }
+    }
+
+    /**
+     * 校验用户是否有数据权限
+     * 
+     * @param userId 用户id
+     */
+    @Override
+    public void checkUserDataScope(Long userId)
+    {
+        if (!SysUser.isAdmin(SecurityUtils.getUserId()))
+        {
+            SysUser user = new SysUser();
+            user.setUserId(userId);
+            List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
+            if (StringUtils.isEmpty(users))
+            {
+                throw new ServiceException("没有权限访问用户数据!");
+            }
+        }
+    }
+
+    /**
+     * 新增保存用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int insertUser(SysUser user)
+    {
+        // 新增用户信息
+        int rows = userMapper.insertUser(user);
+        // 新增用户岗位关联
+        insertUserPost(user);
+        // 新增用户与角色管理
+        insertUserRole(user);
+        return rows;
+    }
+
+    /**
+     * 注册用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    @Override
+    public boolean registerUser(SysUser user)
+    {
+        return userMapper.insertUser(user) > 0;
+    }
+
+    /**
+     * 修改保存用户信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int updateUser(SysUser user)
+    {
+        Long userId = user.getUserId();
+        // 删除用户与角色关联
+        userRoleMapper.deleteUserRoleByUserId(userId);
+        // 新增用户与角色管理
+        insertUserRole(user);
+        // 删除用户与岗位关联
+        userPostMapper.deleteUserPostByUserId(userId);
+        // 新增用户与岗位管理
+        insertUserPost(user);
+        return userMapper.updateUser(user);
+    }
+
+    /**
+     * 用户授权角色
+     * 
+     * @param userId 用户ID
+     * @param roleIds 角色组
+     */
+    @Override
+    @Transactional
+    public void insertUserAuth(Long userId, Long[] roleIds)
+    {
+        userRoleMapper.deleteUserRoleByUserId(userId);
+        insertUserRole(userId, roleIds);
+    }
+
+    /**
+     * 修改用户状态
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    @Override
+    public int updateUserStatus(SysUser user)
+    {
+        return userMapper.updateUser(user);
+    }
+
+    /**
+     * 修改用户基本信息
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    @Override
+    public int updateUserProfile(SysUser user)
+    {
+        return userMapper.updateUser(user);
+    }
+
+    /**
+     * 修改用户头像
+     * 
+     * @param userName 用户名
+     * @param avatar 头像地址
+     * @return 结果
+     */
+    @Override
+    public boolean updateUserAvatar(String userName, String avatar)
+    {
+        return userMapper.updateUserAvatar(userName, avatar) > 0;
+    }
+
+    /**
+     * 重置用户密码
+     * 
+     * @param user 用户信息
+     * @return 结果
+     */
+    @Override
+    public int resetPwd(SysUser user)
+    {
+        return userMapper.updateUser(user);
+    }
+
+    /**
+     * 重置用户密码
+     * 
+     * @param userName 用户名
+     * @param password 密码
+     * @return 结果
+     */
+    @Override
+    public int resetUserPwd(String userName, String password)
+    {
+        return userMapper.resetUserPwd(userName, password);
+    }
+
+    /**
+     * 新增用户角色信息
+     * 
+     * @param user 用户对象
+     */
+    public void insertUserRole(SysUser user)
+    {
+        this.insertUserRole(user.getUserId(), user.getRoleIds());
+    }
+
+    /**
+     * 新增用户岗位信息
+     * 
+     * @param user 用户对象
+     */
+    public void insertUserPost(SysUser user)
+    {
+        Long[] posts = user.getPostIds();
+        if (StringUtils.isNotEmpty(posts))
+        {
+            // 新增用户与岗位管理
+            List<SysUserPost> list = new ArrayList<SysUserPost>(posts.length);
+            for (Long postId : posts)
+            {
+                SysUserPost up = new SysUserPost();
+                up.setUserId(user.getUserId());
+                up.setPostId(postId);
+                list.add(up);
+            }
+            userPostMapper.batchUserPost(list);
+        }
+    }
+
+    /**
+     * 新增用户角色信息
+     * 
+     * @param userId 用户ID
+     * @param roleIds 角色组
+     */
+    public void insertUserRole(Long userId, Long[] roleIds)
+    {
+        if (StringUtils.isNotEmpty(roleIds))
+        {
+            // 新增用户与角色管理
+            List<SysUserRole> list = new ArrayList<SysUserRole>(roleIds.length);
+            for (Long roleId : roleIds)
+            {
+                SysUserRole ur = new SysUserRole();
+                ur.setUserId(userId);
+                ur.setRoleId(roleId);
+                list.add(ur);
+            }
+            userRoleMapper.batchUserRole(list);
+        }
+    }
+
+    /**
+     * 通过用户ID删除用户
+     * 
+     * @param userId 用户ID
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int deleteUserById(Long userId)
+    {
+        // 删除用户与角色关联
+        userRoleMapper.deleteUserRoleByUserId(userId);
+        // 删除用户与岗位表
+//        userPostMapper.deleteUserPostByUserId(userId);
+        return userMapper.deleteUserById(userId);
+    }
+
+    /**
+     * 批量删除用户信息
+     * 
+     * @param userIds 需要删除的用户ID
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public int deleteUserByIds(Long[] userIds)
+    {
+        for (Long userId : userIds)
+        {
+            checkUserAllowed(new SysUser(userId));
+            checkUserDataScope(userId);
+        }
+        // 删除用户与角色关联
+        userRoleMapper.deleteUserRole(userIds);
+        // 删除用户与岗位关联
+        userPostMapper.deleteUserPost(userIds);
+        return userMapper.deleteUserByIds(userIds);
+    }
+
+    /**
+     * 导入用户数据
+     * 
+     * @param userList 用户数据列表
+     * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
+     * @param operName 操作用户
+     * @return 结果
+     */
+    @Override
+    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName)
+    {
+        if (StringUtils.isNull(userList) || userList.size() == 0)
+        {
+            throw new ServiceException("导入用户数据不能为空!");
+        }
+        int successNum = 0;
+        int failureNum = 0;
+        StringBuilder successMsg = new StringBuilder();
+        StringBuilder failureMsg = new StringBuilder();
+        for (SysUser user : userList)
+        {
+            try
+            {
+                // 验证是否存在这个用户
+                SysUser u = userMapper.selectUserByUserName(user.getUserName());
+                if (StringUtils.isNull(u))
+                {
+                    BeanValidators.validateWithException(validator, user);
+                    deptService.checkDeptDataScope(user.getDeptId());
+                    String password = configService.selectConfigByKey("sys.user.initPassword");
+                    user.setPassword(SecurityUtils.encryptPassword(password));
+                    user.setCreateBy(operName);
+                    userMapper.insertUser(user);
+                    successNum++;
+                    successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功");
+                }
+                else if (isUpdateSupport)
+                {
+                    BeanValidators.validateWithException(validator, user);
+                    checkUserAllowed(u);
+                    checkUserDataScope(u.getUserId());
+                    deptService.checkDeptDataScope(user.getDeptId());
+                    user.setUserId(u.getUserId());
+                    user.setUpdateBy(operName);
+                    userMapper.updateUser(user);
+                    successNum++;
+                    successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 更新成功");
+                }
+                else
+                {
+                    failureNum++;
+                    failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName() + " 已存在");
+                }
+            }
+            catch (Exception e)
+            {
+                failureNum++;
+                String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败:";
+                failureMsg.append(msg + e.getMessage());
+                log.error(msg, e);
+            }
+        }
+        if (failureNum > 0)
+        {
+            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
+            throw new ServiceException(failureMsg.toString());
+        }
+        else
+        {
+            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
+        }
+        return successMsg.toString();
+    }
+
+    @Override
+    public IPage<SysUserPageListVO> getSysUserPageList(IPage<SysUserPageListVO> page, String nickName, Integer phone, String status) {
+        return userMapper.getSysUserPageList(page,nickName,phone,status);
+
+    }
+
+    @Override
+    public R<SysUserVO> getInfo(Long userId) {
+        return userMapper.getSysUserVO(userId);
+    }
+
+    @Override
+    public void add(AddSysUserDTO dto) {
+        //检查手机号是否重复
+        SysUser sysUser= userMapper.selectuserByPhone(dto.getPhonenumber());
+        if (sysUser != null){
+            throw new ServiceException("手机号重复");
+        }
+        //部门是否存在
+        SysDept sysDept = deptService.selectDeptById(dto.getDeptId());
+        if (sysDept == null){
+            throw new ServiceException("部门不存在");
+        }
+        //角色是否存在
+        SysRole sysRole = roleMapper.selectRoleById(dto.getRoleId());
+        if (sysRole == null){
+            throw new ServiceException("角色不存在");
+        }
+        //手机号作为登录
+        sysUser = new SysUser();
+        sysUser.setUserName(dto.getPhonenumber());
+        sysUser.setPhonenumber(dto.getPhonenumber());
+        sysUser.setNickName(dto.getNickName());
+        //后六位为默认密码
+        sysUser.setPassword(SecurityUtils.encryptPassword(Md5Utils.hash(dto.getPhonenumber().substring(6,12))));
+        sysUser.setStatus("0");
+        sysUser.setDeptId(dto.getDeptId());  //添加用户-部门关系
+        userMapper.insertUser(sysUser);
+        //添加用户-角色关系
+        userRoleMapper.insert(sysUser.getUserId(),dto.getRoleId());
+
+    }
+
+    @Override
+    public void edit(EditSysUserDTO dto) {
+        //检查用户是否存在
+        SysUser sysUser = userMapper.selectUserById(dto.getUserId());
+        if (sysUser==null){
+            throw new ServiceException("该用户不存在");
+        }
+
+        if (!Objects.equals(dto.getPhonenumber(), sysUser.getPhonenumber())){
+            //修改手机号
+            //检查手机号是否重复
+            SysUser phoneUser= userMapper.selectuserByPhone(dto.getPhonenumber());
+            if (phoneUser != null){
+                throw new ServiceException("手机号重复");
+            }
+            sysUser.setPhonenumber(dto.getPhonenumber());
+            sysUser.setUserName(dto.getPhonenumber());
+        }
+
+        if (!Objects.equals(dto.getDeptId(), sysUser.getDeptId())){
+            //部门是否存在
+            SysDept sysDept = deptService.selectDeptById(dto.getDeptId());
+            if (sysDept == null){
+                throw new ServiceException("部门不存在");
+            }
+            sysUser.setDeptId(dto.getDeptId());
+        }
+
+        SysRole sysRole= userRoleMapper.selectRoleByUserId(dto.getUserId());
+        if (!Objects.equals(sysRole.getRoleId(), dto.getRoleId())){
+            //角色是否存在
+            SysRole selectRole = roleMapper.selectRoleById(dto.getRoleId());
+            if (selectRole == null){
+                throw new ServiceException("角色不存在");
+            }
+            sysUser.setRoleId(dto.getRoleId());
+            //修改用户-角色关系
+            userRoleMapper.deleteUserRoleByUserId(dto.getUserId());
+            userRoleMapper.insert(sysUser.getUserId(),dto.getRoleId());
+        }
+        sysUser.setNickName(dto.getNickName());
+        userMapper.updateUser(sysUser);
+    }
+
+    @Override
+    public void resetPassword(Long userId) {
+        //重置密码
+        //检查用户是否存在
+        SysUser sysUser = userMapper.selectUserById(userId);
+        if (sysUser==null){
+            throw new ServiceException("该用户不存在");
+        }
+        sysUser.setPassword(SecurityUtils.encryptPassword(Md5Utils.hash(sysUser.getPassword().substring(6,12))));
+        userMapper.updateUser(sysUser);
+    }
+
+    @Override
+    public void changeStatus(Long userId) {
+        //检查用户是否存在
+        SysUser sysUser = userMapper.selectUserById(userId);
+        if (sysUser==null){
+            throw new ServiceException("该用户不存在");
+        }
+        sysUser.setStatus(Objects.equals(sysUser.getStatus(), "1") ?"0":"1");
+        userMapper.updateUser(sysUser);
+    }
+}
diff --git a/pt-system/src/main/resources/mapper/system/SysConfigMapper.xml b/pt-system/src/main/resources/mapper/system/SysConfigMapper.xml
new file mode 100644
index 0000000..a5ff114
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysConfigMapper.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysConfigMapper">
+    
+    <resultMap type="SysConfig" id="SysConfigResult">
+    	<id     property="configId"      column="config_id"      />
+        <result property="configName"    column="config_name"    />
+        <result property="configKey"     column="config_key"     />
+        <result property="configValue"   column="config_value"   />
+        <result property="configType"    column="config_type"    />
+        <result property="createBy"      column="create_by"      />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"      column="update_by"      />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+    
+    <sql id="selectConfigVo">
+        select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark 
+		from sys_config
+    </sql>
+    
+    <!-- 查询条件 -->
+	<sql id="sqlwhereSearch">
+		<where>
+			<if test="configId !=null">
+				and config_id = #{configId}
+			</if>
+			<if test="configKey !=null and configKey != ''">
+				and config_key = #{configKey}
+			</if>
+		</where>
+	</sql>
+    
+    <select id="selectConfig" parameterType="SysConfig" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        <include refid="sqlwhereSearch"/>
+    </select>
+    
+    <select id="selectConfigList" parameterType="SysConfig" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        <where>
+			<if test="configName != null and configName != ''">
+				AND config_name like concat('%', #{configName}, '%')
+			</if>
+			<if test="configType != null and configType != ''">
+				AND config_type = #{configType}
+			</if>
+			<if test="configKey != null and configKey != ''">
+				AND config_key like concat('%', #{configKey}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+			</if>
+		</where>
+    </select>
+    
+    <select id="selectConfigById" parameterType="Long" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        where config_id = #{configId}
+    </select>
+	
+    <select id="checkConfigKeyUnique" parameterType="String" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        where config_key = #{configKey} limit 1
+    </select>
+    
+    <insert id="insertConfig" parameterType="SysConfig">
+        insert into sys_config (
+			<if test="configName != null and configName != '' ">config_name,</if>
+			<if test="configKey != null and configKey != '' ">config_key,</if>
+			<if test="configValue != null and configValue != '' ">config_value,</if>
+			<if test="configType != null and configType != '' ">config_type,</if>
+			<if test="createBy != null and createBy != ''">create_by,</if>
+			<if test="remark != null and remark != ''">remark,</if>
+ 			create_time
+        )values(
+			<if test="configName != null and configName != ''">#{configName},</if>
+			<if test="configKey != null and configKey != ''">#{configKey},</if>
+			<if test="configValue != null and configValue != ''">#{configValue},</if>
+			<if test="configType != null and configType != ''">#{configType},</if>
+			<if test="createBy != null and createBy != ''">#{createBy},</if>
+			<if test="remark != null and remark != ''">#{remark},</if>
+ 			sysdate()
+		)
+    </insert>
+	 
+    <update id="updateConfig" parameterType="SysConfig">
+        update sys_config 
+        <set>
+            <if test="configName != null and configName != ''">config_name = #{configName},</if>
+            <if test="configKey != null and configKey != ''">config_key = #{configKey},</if>
+            <if test="configValue != null and configValue != ''">config_value = #{configValue},</if>
+            <if test="configType != null and configType != ''">config_type = #{configType},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+ 			update_time = sysdate()
+        </set>
+        where config_id = #{configId}
+    </update>
+	
+    <delete id="deleteConfigById" parameterType="Long">
+        delete from sys_config where config_id = #{configId}
+    </delete>
+    
+    <delete id="deleteConfigByIds" parameterType="Long">
+        delete from sys_config where config_id in 
+        <foreach item="configId" collection="array" open="(" separator="," close=")">
+        	#{configId}
+        </foreach>
+    </delete>
+    
+</mapper>
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysDeptMapper.xml b/pt-system/src/main/resources/mapper/system/SysDeptMapper.xml
new file mode 100644
index 0000000..3120faa
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysDeptMapper.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysDeptMapper">
+
+	<resultMap type="SysDept" id="SysDeptResult">
+		<id     property="deptId"     column="dept_id"     />
+		<result property="parentId"   column="parent_id"   />
+		<result property="ancestors"  column="ancestors"   />
+		<result property="deptName"   column="dept_name"   />
+		<result property="orderNum"   column="order_num"   />
+		<result property="leader"     column="leader"      />
+		<result property="phone"      column="phone"       />
+		<result property="email"      column="email"       />
+		<result property="status"     column="status"      />
+		<result property="delFlag"    column="del_flag"    />
+		<result property="parentName" column="parent_name" />
+		<result property="createBy"   column="create_by"   />
+		<result property="createTime" column="create_time" />
+		<result property="updateBy"   column="update_by"   />
+		<result property="updateTime" column="update_time" />
+	</resultMap>
+	
+	<sql id="selectDeptVo">
+        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time 
+        from sys_dept d
+    </sql>
+    
+	<select id="selectDeptList" parameterType="SysDept" resultMap="SysDeptResult">
+        <include refid="selectDeptVo"/>
+        where d.del_flag = '0'
+		<if test="deptId != null and deptId != 0">
+			AND dept_id = #{deptId}
+		</if>
+        <if test="parentId != null and parentId != 0">
+			AND parent_id = #{parentId}
+		</if>
+		<if test="deptName != null and deptName != ''">
+			AND dept_name like concat('%', #{deptName}, '%')
+		</if>
+		<if test="status != null and status != ''">
+			AND status = #{status}
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+		order by d.parent_id, d.order_num
+    </select>
+    
+    <select id="selectDeptListByRoleId" resultType="Long">
+		select d.dept_id
+		from sys_dept d
+            left join sys_role_dept rd on d.dept_id = rd.dept_id
+        where rd.role_id = #{roleId}
+            <if test="deptCheckStrictly">
+              and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId})
+            </if>
+		order by d.parent_id, d.order_num
+	</select>
+    
+    <select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
+		select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,
+			(select dept_name from sys_dept where dept_id = d.parent_id) parent_name
+		from sys_dept d
+		where d.dept_id = #{deptId}
+	</select>
+    
+    <select id="checkDeptExistUser" parameterType="Long" resultType="int">
+		select count(1) from sys_user where dept_id = #{deptId} and del_flag = '0'
+	</select>
+	
+	<select id="hasChildByDeptId" parameterType="Long" resultType="int">
+		select count(1) from sys_dept
+		where del_flag = '0' and parent_id = #{deptId} limit 1
+	</select>
+	
+	<select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult">
+		select * from sys_dept where find_in_set(#{deptId}, ancestors)
+	</select>
+	
+	<select id="selectNormalChildrenDeptById" parameterType="Long" resultType="int">
+		select count(*) from sys_dept where status = 0 and del_flag = '0' and find_in_set(#{deptId}, ancestors)
+	</select>
+	
+	<select id="checkDeptNameUnique" resultMap="SysDeptResult">
+	    <include refid="selectDeptVo"/>
+		where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
+	</select>
+    <select id="page" resultType="com.ruoyi.system.object.vo.SysDeptPageVO">
+		select
+			dept_id,name
+		from sys_dept
+		where
+		    del_flag='0'
+	</select>
+	<select id="getDeptList" resultType="com.ruoyi.system.object.vo.SysDeptPageVO">
+		select
+			dept_id,name
+		from sys_dept
+		where
+			del_flag='0'
+
+	</select>
+	<select id="selectDeptByName" resultType="com.ruoyi.common.core.domain.entity.SysDept">
+		select dept_id from sys_dept where del_flag='0' and deptName like #{name}
+	</select>
+
+	<insert id="insertDept" parameterType="SysDept">
+ 		insert into sys_dept(
+ 			<if test="deptId != null and deptId != 0">dept_id,</if>
+ 			<if test="parentId != null and parentId != 0">parent_id,</if>
+ 			<if test="deptName != null and deptName != ''">dept_name,</if>
+ 			<if test="ancestors != null and ancestors != ''">ancestors,</if>
+ 			<if test="orderNum != null">order_num,</if>
+ 			<if test="leader != null and leader != ''">leader,</if>
+ 			<if test="phone != null and phone != ''">phone,</if>
+ 			<if test="email != null and email != ''">email,</if>
+ 			<if test="status != null">status,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="deptId != null and deptId != 0">#{deptId},</if>
+ 			<if test="parentId != null and parentId != 0">#{parentId},</if>
+ 			<if test="deptName != null and deptName != ''">#{deptName},</if>
+ 			<if test="ancestors != null and ancestors != ''">#{ancestors},</if>
+ 			<if test="orderNum != null">#{orderNum},</if>
+ 			<if test="leader != null and leader != ''">#{leader},</if>
+ 			<if test="phone != null and phone != ''">#{phone},</if>
+ 			<if test="email != null and email != ''">#{email},</if>
+ 			<if test="status != null">#{status},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<update id="updateDept" parameterType="SysDept">
+ 		update sys_dept
+ 		<set>
+ 			<if test="parentId != null and parentId != 0">parent_id = #{parentId},</if>
+ 			<if test="deptName != null and deptName != ''">dept_name = #{deptName},</if>
+ 			<if test="ancestors != null and ancestors != ''">ancestors = #{ancestors},</if>
+ 			<if test="orderNum != null">order_num = #{orderNum},</if>
+ 			<if test="leader != null">leader = #{leader},</if>
+ 			<if test="phone != null">phone = #{phone},</if>
+ 			<if test="email != null">email = #{email},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where dept_id = #{deptId}
+	</update>
+	
+	<update id="updateDeptChildren" parameterType="java.util.List">
+	    update sys_dept set ancestors =
+	    <foreach collection="depts" item="item" index="index"
+	        separator=" " open="case dept_id" close="end">
+	        when #{item.deptId} then #{item.ancestors}
+	    </foreach>
+	    where dept_id in
+	    <foreach collection="depts" item="item" index="index"
+	        separator="," open="(" close=")">
+	        #{item.deptId}
+	    </foreach>
+	</update>
+	 
+	<update id="updateDeptStatusNormal" parameterType="Long">
+ 	    update sys_dept set status = '0' where dept_id in 
+ 	    <foreach collection="array" item="deptId" open="(" separator="," close=")">
+        	#{deptId}
+        </foreach>
+	</update>
+	<update id="updateDeptName">
+		update sys_dept set dept_name = #{sysDept.deptName} where dept_id = #{sysDept.deptId}
+	</update>
+
+	<delete id="deleteDeptById" parameterType="Long">
+		update sys_dept set del_flag = '2' where dept_id = #{deptId}
+	</delete>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/pt-system/src/main/resources/mapper/system/SysDictDataMapper.xml
new file mode 100644
index 0000000..3b94b7f
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysDictDataMapper.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysDictDataMapper">
+	
+	<resultMap type="SysDictData" id="SysDictDataResult">
+		<id     property="dictCode"   column="dict_code"   />
+		<result property="dictSort"   column="dict_sort"   />
+		<result property="dictLabel"  column="dict_label"  />
+		<result property="dictValue"  column="dict_value"  />
+		<result property="dictType"   column="dict_type"   />
+		<result property="cssClass"   column="css_class"   />
+		<result property="listClass"  column="list_class"  />
+		<result property="isDefault"  column="is_default"  />
+		<result property="status"     column="status"      />
+		<result property="createBy"   column="create_by"   />
+		<result property="createTime" column="create_time" />
+		<result property="updateBy"   column="update_by"   />
+		<result property="updateTime" column="update_time" />
+	</resultMap>
+	
+	<sql id="selectDictDataVo">
+        select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark 
+		from sys_dict_data
+    </sql>
+
+	<select id="selectDictDataList" parameterType="SysDictData" resultMap="SysDictDataResult">
+	    <include refid="selectDictDataVo"/>
+		<where>
+		    <if test="dictType != null and dictType != ''">
+				AND dict_type = #{dictType}
+			</if>
+			<if test="dictLabel != null and dictLabel != ''">
+				AND dict_label like concat('%', #{dictLabel}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+		</where>
+		order by dict_sort asc
+	</select>
+	
+	<select id="selectDictDataByType" parameterType="String" resultMap="SysDictDataResult">
+		<include refid="selectDictDataVo"/>
+		where status = '0' and dict_type = #{dictType} order by dict_sort asc
+	</select>
+	
+	<select id="selectDictLabel" resultType="String">
+		select dict_label from sys_dict_data
+		where dict_type = #{dictType} and dict_value = #{dictValue}
+	</select>
+	
+	<select id="selectDictDataById" parameterType="Long" resultMap="SysDictDataResult">
+		<include refid="selectDictDataVo"/>
+		where dict_code = #{dictCode}
+	</select>
+	
+	<select id="countDictDataByType" resultType="Integer">
+	    select count(1) from sys_dict_data where dict_type=#{dictType}  
+	</select>
+	
+	<delete id="deleteDictDataById" parameterType="Long">
+ 		delete from sys_dict_data where dict_code = #{dictCode}
+ 	</delete>
+ 	
+ 	<delete id="deleteDictDataByIds" parameterType="Long">
+ 		delete from sys_dict_data where dict_code in
+ 		<foreach collection="array" item="dictCode" open="(" separator="," close=")">
+ 			#{dictCode}
+        </foreach> 
+ 	</delete>
+	
+	<update id="updateDictData" parameterType="SysDictData">
+ 		update sys_dict_data
+ 		<set>
+ 			<if test="dictSort != null">dict_sort = #{dictSort},</if>
+ 			<if test="dictLabel != null and dictLabel != ''">dict_label = #{dictLabel},</if>
+ 			<if test="dictValue != null and dictValue != ''">dict_value = #{dictValue},</if>
+ 			<if test="dictType != null and dictType != ''">dict_type = #{dictType},</if>
+ 			<if test="cssClass != null">css_class = #{cssClass},</if>
+ 			<if test="listClass != null">list_class = #{listClass},</if>
+ 			<if test="isDefault != null and isDefault != ''">is_default = #{isDefault},</if>
+ 			<if test="status != null">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where dict_code = #{dictCode}
+	</update>
+	
+	<update id="updateDictDataType" parameterType="String">
+ 		update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType}
+	</update>
+ 	
+ 	<insert id="insertDictData" parameterType="SysDictData">
+ 		insert into sys_dict_data(
+ 			<if test="dictSort != null">dict_sort,</if>
+ 			<if test="dictLabel != null and dictLabel != ''">dict_label,</if>
+ 			<if test="dictValue != null and dictValue != ''">dict_value,</if>
+ 			<if test="dictType != null and dictType != ''">dict_type,</if>
+ 			<if test="cssClass != null and cssClass != ''">css_class,</if>
+ 			<if test="listClass != null and listClass != ''">list_class,</if>
+ 			<if test="isDefault != null and isDefault != ''">is_default,</if>
+ 			<if test="status != null">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 		    <if test="dictSort != null">#{dictSort},</if>
+ 		    <if test="dictLabel != null and dictLabel != ''">#{dictLabel},</if>
+ 			<if test="dictValue != null and dictValue != ''">#{dictValue},</if>
+ 			<if test="dictType != null and dictType != ''">#{dictType},</if>
+ 			<if test="cssClass != null and cssClass != ''">#{cssClass},</if>
+ 			<if test="listClass != null and listClass != ''">#{listClass},</if>
+ 			<if test="isDefault != null and isDefault != ''">#{isDefault},</if>
+ 			<if test="status != null">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/pt-system/src/main/resources/mapper/system/SysDictTypeMapper.xml
new file mode 100644
index 0000000..438d484
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysDictTypeMapper.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysDictTypeMapper">
+
+	<resultMap type="SysDictType" id="SysDictTypeResult">
+		<id     property="dictId"     column="dict_id"     />
+		<result property="dictName"   column="dict_name"   />
+		<result property="dictType"   column="dict_type"   />
+		<result property="status"     column="status"      />
+		<result property="createBy"   column="create_by"   />
+		<result property="createTime" column="create_time" />
+		<result property="updateBy"   column="update_by"   />
+		<result property="updateTime" column="update_time" />
+	</resultMap>
+	
+	<sql id="selectDictTypeVo">
+        select dict_id, dict_name, dict_type, status, create_by, create_time, remark 
+		from sys_dict_type
+    </sql>
+
+	<select id="selectDictTypeList" parameterType="SysDictType" resultMap="SysDictTypeResult">
+	    <include refid="selectDictTypeVo"/>
+		<where>
+		    <if test="dictName != null and dictName != ''">
+				AND dict_name like concat('%', #{dictName}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="dictType != null and dictType != ''">
+				AND dict_type like concat('%', #{dictType}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+			</if>
+	    </where>
+	</select>
+	
+	<select id="selectDictTypeAll" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+	</select>
+	
+	<select id="selectDictTypeById" parameterType="Long" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+		where dict_id = #{dictId}
+	</select>
+	
+	<select id="selectDictTypeByType" parameterType="String" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+		where dict_type = #{dictType}
+	</select>
+	
+	<select id="checkDictTypeUnique" parameterType="String" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+		where dict_type = #{dictType} limit 1
+	</select>
+	
+	<delete id="deleteDictTypeById" parameterType="Long">
+ 		delete from sys_dict_type where dict_id = #{dictId}
+ 	</delete>
+ 	
+ 	<delete id="deleteDictTypeByIds" parameterType="Long">
+ 		delete from sys_dict_type where dict_id in
+ 		<foreach collection="array" item="dictId" open="(" separator="," close=")">
+ 			#{dictId}
+        </foreach> 
+ 	</delete>
+
+ 	<update id="updateDictType" parameterType="SysDictType">
+ 		update sys_dict_type
+ 		<set>
+ 			<if test="dictName != null and dictName != ''">dict_name = #{dictName},</if>
+ 			<if test="dictType != null and dictType != ''">dict_type = #{dictType},</if>
+ 			<if test="status != null">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where dict_id = #{dictId}
+	</update>
+ 	
+ 	<insert id="insertDictType" parameterType="SysDictType">
+ 		insert into sys_dict_type(
+ 			<if test="dictName != null and dictName != ''">dict_name,</if>
+ 			<if test="dictType != null and dictType != ''">dict_type,</if>
+ 			<if test="status != null">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="dictName != null and dictName != ''">#{dictName},</if>
+ 			<if test="dictType != null and dictType != ''">#{dictType},</if>
+ 			<if test="status != null">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/pt-system/src/main/resources/mapper/system/SysLogininforMapper.xml
new file mode 100644
index 0000000..822d665
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysLogininforMapper.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysLogininforMapper">
+
+	<resultMap type="SysLogininfor" id="SysLogininforResult">
+		<id     property="infoId"        column="info_id"           />
+		<result property="userName"      column="user_name"         />
+		<result property="status"        column="status"            />
+		<result property="ipaddr"        column="ipaddr"            />
+		<result property="loginLocation" column="login_location"    />
+		<result property="browser"       column="browser"           />
+		<result property="os"            column="os"                />
+		<result property="msg"           column="msg"               />
+		<result property="loginTime"     column="login_time"        />
+	</resultMap>
+
+	<insert id="insertLogininfor" parameterType="SysLogininfor">
+		insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time)
+		values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate())
+	</insert>
+	
+	<select id="selectLogininforList" parameterType="SysLogininfor" resultMap="SysLogininforResult">
+		select info_id, user_name, ipaddr, login_location, browser, os, status, msg, login_time from sys_logininfor
+		<where>
+			<if test="ipaddr != null and ipaddr != ''">
+				AND ipaddr like concat('%', #{ipaddr}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="userName != null and userName != ''">
+				AND user_name like concat('%', #{userName}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				AND login_time &gt;= #{params.beginTime}
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				AND login_time &lt;= #{params.endTime}
+			</if>
+		</where>
+		order by info_id desc
+	</select>
+	
+	<delete id="deleteLogininforByIds" parameterType="Long">
+ 		delete from sys_logininfor where info_id in
+ 		<foreach collection="array" item="infoId" open="(" separator="," close=")">
+ 			#{infoId}
+        </foreach> 
+ 	</delete>
+    
+    <update id="cleanLogininfor">
+        truncate table sys_logininfor
+    </update>
+    
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysMenuMapper.xml b/pt-system/src/main/resources/mapper/system/SysMenuMapper.xml
new file mode 100644
index 0000000..c063570
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysMenuMapper.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+		PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+		"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysMenuMapper">
+
+	<resultMap type="SysMenu" id="SysMenuResult">
+		<id     property="menuId"         column="menu_id"        />
+		<result property="menuName"       column="menu_name"      />
+		<result property="parentName"     column="parent_name"    />
+		<result property="parentId"       column="parent_id"      />
+		<result property="orderNum"       column="order_num"      />
+		<result property="path"           column="path"           />
+		<result property="component"      column="component"      />
+		<result property="query"          column="query"          />
+		<result property="routeName"      column="route_name"     />
+		<result property="isFrame"        column="is_frame"       />
+		<result property="isCache"        column="is_cache"       />
+		<result property="menuType"       column="menu_type"      />
+		<result property="visible"        column="visible"        />
+		<result property="status"         column="status"         />
+		<result property="perms"          column="perms"          />
+		<result property="icon"           column="icon"           />
+		<result property="createBy"       column="create_by"      />
+		<result property="createTime"     column="create_time"    />
+		<result property="updateTime"     column="update_time"    />
+		<result property="updateBy"       column="update_by"      />
+		<result property="remark"         column="remark"         />
+	</resultMap>
+
+	<sql id="selectMenuVo">
+        select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time 
+		from sys_menu
+    </sql>
+    
+    <select id="selectMenuList" parameterType="SysMenu" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		<where>
+			<if test="menuName != null and menuName != ''">
+				AND menu_name like concat('%', #{menuName}, '%')
+			</if>
+			<if test="visible != null and visible != ''">
+				AND visible = #{visible}
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+		</where>
+		order by parent_id, order_num
+	</select>
+	
+	<select id="selectMenuTreeAll" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		from sys_menu m where m.menu_type in ('M', 'C') and m.status = 0
+		order by m.parent_id, m.order_num
+	</select>
+	
+	<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		from sys_menu m
+		left join sys_role_menu rm on m.menu_id = rm.menu_id
+		left join sys_user_role ur on rm.role_id = ur.role_id
+		left join sys_role ro on ur.role_id = ro.role_id
+		where ur.user_id = #{params.userId}
+		<if test="menuName != null and menuName != ''">
+            AND m.menu_name like concat('%', #{menuName}, '%')
+		</if>
+		<if test="visible != null and visible != ''">
+            AND m.visible = #{visible}
+		</if>
+		<if test="status != null and status != ''">
+            AND m.status = #{status}
+		</if>
+		order by m.parent_id, m.order_num
+	</select>
+    
+    <select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+			 left join sys_user_role ur on rm.role_id = ur.role_id
+			 left join sys_role ro on ur.role_id = ro.role_id
+			 left join sys_user u on ur.user_id = u.user_id
+		where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = 0  AND ro.status = 0
+		order by m.parent_id, m.order_num
+	</select>
+	
+	<select id="selectMenuListByRoleId" resultType="Long">
+		select m.menu_id
+		from sys_menu m
+            left join sys_role_menu rm on m.menu_id = rm.menu_id
+        where rm.role_id = #{roleId}
+            <if test="menuCheckStrictly">
+              and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = #{roleId})
+            </if>
+		order by m.parent_id, m.order_num
+	</select>
+	
+	<select id="selectMenuPerms" resultType="String">
+		select distinct m.perms
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+			 left join sys_user_role ur on rm.role_id = ur.role_id
+	</select>
+
+	<select id="selectMenuPermsByUserId" parameterType="Long" resultType="String">
+		select distinct m.perms
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+			 left join sys_user_role ur on rm.role_id = ur.role_id
+			 left join sys_role r on r.role_id = ur.role_id
+		where m.status = '0' and r.status = '0' and ur.user_id = #{userId}
+	</select>
+	
+	<select id="selectMenuPermsByRoleId" parameterType="Long" resultType="String">
+		select distinct m.perms
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+		where m.status = '0' and rm.role_id = #{roleId}
+	</select>
+	
+	<select id="selectMenuById" parameterType="Long" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		where menu_id = #{menuId}
+	</select>
+	
+	<select id="hasChildByMenuId" resultType="Integer">
+	    select count(1) from sys_menu where parent_id = #{menuId}  
+	</select>
+	
+	<select id="checkMenuNameUnique" parameterType="SysMenu" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		where menu_name=#{menuName} and parent_id = #{parentId} limit 1
+	</select>
+
+	<select id="getAllRootMenu" resultType="com.ruoyi.system.object.vo.MenuTreeVO">
+		select distinct sm.menu_id,sm.menu_name,sm.path,sm.menu_type
+		from  sys_menu  sm
+		where
+		sm.parent_id = 0 order by sm.order_num 	asc
+	</select>
+
+	<select id="selectMenusByParentId" resultType="com.ruoyi.system.object.vo.MenuTreeVO">
+		select menu_id,menu_name,path,menu_type from sys_menu
+		where parent_id = #{menuId} order by order_num 	asc
+	</select>
+
+	<update id="updateMenu" parameterType="SysMenu">
+		update sys_menu
+		<set>
+			<if test="menuName != null and menuName != ''">menu_name = #{menuName},</if>
+			<if test="parentId != null">parent_id = #{parentId},</if>
+			<if test="orderNum != null">order_num = #{orderNum},</if>
+			<if test="path != null and path != ''">path = #{path},</if>
+			<if test="component != null">component = #{component},</if>
+			<if test="query != null">`query` = #{query},</if>
+			<if test="routeName != null">route_name = #{routeName},</if>
+			<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
+			<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
+			<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>
+			<if test="visible != null">visible = #{visible},</if>
+			<if test="status != null">status = #{status},</if>
+			<if test="perms !=null">perms = #{perms},</if>
+			<if test="icon !=null and icon != ''">icon = #{icon},</if>
+			<if test="remark != null and remark != ''">remark = #{remark},</if>
+			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+			update_time = sysdate()
+		</set>
+		where menu_id = #{menuId}
+	</update>
+
+	<insert id="insertMenu" parameterType="SysMenu">
+		insert into sys_menu(
+		<if test="menuId != null and menuId != 0">menu_id,</if>
+		<if test="parentId != null and parentId != 0">parent_id,</if>
+		<if test="menuName != null and menuName != ''">menu_name,</if>
+		<if test="orderNum != null">order_num,</if>
+		<if test="path != null and path != ''">path,</if>
+		<if test="component != null and component != ''">component,</if>
+		<if test="query != null and query != ''">`query`,</if>
+		<if test="routeName != null">route_name,</if>
+		<if test="isFrame != null and isFrame != ''">is_frame,</if>
+		<if test="isCache != null and isCache != ''">is_cache,</if>
+		<if test="menuType != null and menuType != ''">menu_type,</if>
+		<if test="visible != null">visible,</if>
+		<if test="status != null">status,</if>
+		<if test="perms !=null and perms != ''">perms,</if>
+		<if test="icon != null and icon != ''">icon,</if>
+		<if test="remark != null and remark != ''">remark,</if>
+		<if test="createBy != null and createBy != ''">create_by,</if>
+		create_time
+		)values(
+		<if test="menuId != null and menuId != 0">#{menuId},</if>
+		<if test="parentId != null and parentId != 0">#{parentId},</if>
+		<if test="menuName != null and menuName != ''">#{menuName},</if>
+		<if test="orderNum != null">#{orderNum},</if>
+		<if test="path != null and path != ''">#{path},</if>
+		<if test="component != null and component != ''">#{component},</if>
+		<if test="query != null and query != ''">#{query},</if>
+		<if test="routeName != null">#{routeName},</if>
+		<if test="isFrame != null and isFrame != ''">#{isFrame},</if>
+		<if test="isCache != null and isCache != ''">#{isCache},</if>
+		<if test="menuType != null and menuType != ''">#{menuType},</if>
+		<if test="visible != null">#{visible},</if>
+		<if test="status != null">#{status},</if>
+		<if test="perms !=null and perms != ''">#{perms},</if>
+		<if test="icon != null and icon != ''">#{icon},</if>
+		<if test="remark != null and remark != ''">#{remark},</if>
+		<if test="createBy != null and createBy != ''">#{createBy},</if>
+		sysdate()
+		)
+	</insert>
+	
+	<delete id="deleteMenuById" parameterType="Long">
+	    delete from sys_menu where menu_id = #{menuId}
+	</delete>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/pt-system/src/main/resources/mapper/system/SysNoticeMapper.xml
new file mode 100644
index 0000000..65d3079
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysNoticeMapper.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysNoticeMapper">
+    
+    <resultMap type="SysNotice" id="SysNoticeResult">
+        <result property="noticeId"       column="notice_id"       />
+        <result property="noticeTitle"    column="notice_title"    />
+        <result property="noticeType"     column="notice_type"     />
+        <result property="noticeContent"  column="notice_content"  />
+        <result property="status"         column="status"          />
+        <result property="createBy"       column="create_by"       />
+        <result property="createTime"     column="create_time"     />
+        <result property="updateBy"       column="update_by"       />
+        <result property="updateTime"     column="update_time"     />
+        <result property="remark"         column="remark"          />
+    </resultMap>
+    
+    <sql id="selectNoticeVo">
+        select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark 
+		from sys_notice
+    </sql>
+    
+    <select id="selectNoticeById" parameterType="Long" resultMap="SysNoticeResult">
+        <include refid="selectNoticeVo"/>
+        where notice_id = #{noticeId}
+    </select>
+    
+    <select id="selectNoticeList" parameterType="SysNotice" resultMap="SysNoticeResult">
+        <include refid="selectNoticeVo"/>
+        <where>
+			<if test="noticeTitle != null and noticeTitle != ''">
+				AND notice_title like concat('%', #{noticeTitle}, '%')
+			</if>
+			<if test="noticeType != null and noticeType != ''">
+				AND notice_type = #{noticeType}
+			</if>
+			<if test="createBy != null and createBy != ''">
+				AND create_by like concat('%', #{createBy}, '%')
+			</if>
+		</where>
+    </select>
+    
+    <insert id="insertNotice" parameterType="SysNotice">
+        insert into sys_notice (
+			<if test="noticeTitle != null and noticeTitle != '' ">notice_title, </if>
+			<if test="noticeType != null and noticeType != '' ">notice_type, </if>
+			<if test="noticeContent != null and noticeContent != '' ">notice_content, </if>
+			<if test="status != null and status != '' ">status, </if>
+			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+			<if test="noticeTitle != null and noticeTitle != ''">#{noticeTitle}, </if>
+			<if test="noticeType != null and noticeType != ''">#{noticeType}, </if>
+			<if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if>
+			<if test="status != null and status != ''">#{status}, </if>
+			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+		)
+    </insert>
+	 
+    <update id="updateNotice" parameterType="SysNotice">
+        update sys_notice 
+        <set>
+            <if test="noticeTitle != null and noticeTitle != ''">notice_title = #{noticeTitle}, </if>
+            <if test="noticeType != null and noticeType != ''">notice_type = #{noticeType}, </if>
+            <if test="noticeContent != null">notice_content = #{noticeContent}, </if>
+            <if test="status != null and status != ''">status = #{status}, </if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+        </set>
+        where notice_id = #{noticeId}
+    </update>
+	
+    <delete id="deleteNoticeById" parameterType="Long">
+        delete from sys_notice where notice_id = #{noticeId}
+    </delete>
+    
+    <delete id="deleteNoticeByIds" parameterType="Long">
+        delete from sys_notice where notice_id in 
+        <foreach item="noticeId" collection="array" open="(" separator="," close=")">
+            #{noticeId}
+        </foreach>
+    </delete>
+    
+</mapper>
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/pt-system/src/main/resources/mapper/system/SysOperLogMapper.xml
new file mode 100644
index 0000000..201db07
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysOperLogMapper.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysOperLogMapper">
+
+	<resultMap type="SysOperLog" id="SysOperLogResult">
+		<id     property="operId"         column="oper_id"        />
+		<result property="title"          column="title"          />
+		<result property="businessType"   column="business_type"  />
+		<result property="method"         column="method"         />
+		<result property="requestMethod"  column="request_method" />
+		<result property="operatorType"   column="operator_type"  />
+		<result property="operName"       column="oper_name"      />
+		<result property="deptName"       column="dept_name"      />
+		<result property="operUrl"        column="oper_url"       />
+		<result property="operIp"         column="oper_ip"        />
+		<result property="operLocation"   column="oper_location"  />
+		<result property="operParam"      column="oper_param"     />
+		<result property="jsonResult"     column="json_result"    />
+		<result property="status"         column="status"         />
+		<result property="errorMsg"       column="error_msg"      />
+		<result property="operTime"       column="oper_time"      />
+		<result property="costTime"       column="cost_time"      />
+	</resultMap>
+
+	<sql id="selectOperLogVo">
+        select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time
+        from sys_oper_log
+    </sql>
+    
+	<insert id="insertOperlog" parameterType="SysOperLog">
+		insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time)
+        values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate())
+	</insert>
+	
+	<select id="selectOperLogList" parameterType="SysOperLog" resultMap="SysOperLogResult">
+		<include refid="selectOperLogVo"/>
+		<where>
+			<if test="operIp != null and operIp != ''">
+				AND oper_ip like concat('%', #{operIp}, '%')
+			</if>
+			<if test="title != null and title != ''">
+				AND title like concat('%', #{title}, '%')
+			</if>
+			<if test="businessType != null">
+				AND business_type = #{businessType}
+			</if>
+			<if test="businessTypes != null and businessTypes.length > 0">
+			    AND business_type in
+			    <foreach collection="businessTypes" item="businessType" open="(" separator="," close=")">
+		 			#{businessType}
+		        </foreach> 
+			</if>
+			<if test="status != null">
+				AND status = #{status}
+			</if>
+			<if test="operName != null and operName != ''">
+				AND oper_name like concat('%', #{operName}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				AND oper_time &gt;= #{params.beginTime}
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				AND oper_time &lt;= #{params.endTime}
+			</if>
+		</where>
+		order by oper_id desc
+	</select>
+	
+	<delete id="deleteOperLogByIds" parameterType="Long">
+ 		delete from sys_oper_log where oper_id in
+ 		<foreach collection="array" item="operId" open="(" separator="," close=")">
+ 			#{operId}
+        </foreach> 
+ 	</delete>
+ 	
+ 	<select id="selectOperLogById" parameterType="Long" resultMap="SysOperLogResult">
+		<include refid="selectOperLogVo"/>
+		where oper_id = #{operId}
+	</select>
+	
+	<update id="cleanOperLog">
+        truncate table sys_oper_log
+    </update>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysPostMapper.xml b/pt-system/src/main/resources/mapper/system/SysPostMapper.xml
new file mode 100644
index 0000000..227c459
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysPostMapper.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysPostMapper">
+
+	<resultMap type="SysPost" id="SysPostResult">
+		<id     property="postId"        column="post_id"       />
+		<result property="postCode"      column="post_code"     />
+		<result property="postName"      column="post_name"     />
+		<result property="postSort"      column="post_sort"     />
+		<result property="status"        column="status"        />
+		<result property="createBy"      column="create_by"     />
+		<result property="createTime"    column="create_time"   />
+		<result property="updateBy"      column="update_by"     />
+		<result property="updateTime"    column="update_time"   />
+		<result property="remark"        column="remark"        />
+	</resultMap>
+	
+	<sql id="selectPostVo">
+        select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark 
+		from sys_post
+    </sql>
+	
+	<select id="selectPostList" parameterType="SysPost" resultMap="SysPostResult">
+	    <include refid="selectPostVo"/>
+		<where>
+			<if test="postCode != null and postCode != ''">
+				AND post_code like concat('%', #{postCode}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="postName != null and postName != ''">
+				AND post_name like concat('%', #{postName}, '%')
+			</if>
+		</where>
+	</select>
+	
+	<select id="selectPostAll" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+	</select>
+	
+	<select id="selectPostById" parameterType="Long" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+		where post_id = #{postId}
+	</select>
+	
+	<select id="selectPostListByUserId" parameterType="Long" resultType="Long">
+		select p.post_id
+        from sys_post p
+	        left join sys_user_post up on up.post_id = p.post_id
+	        left join sys_user u on u.user_id = up.user_id
+	    where u.user_id = #{userId}
+	</select>
+	
+	<select id="selectPostsByUserName" parameterType="String" resultMap="SysPostResult">
+		select p.post_id, p.post_name, p.post_code
+		from sys_post p
+			 left join sys_user_post up on up.post_id = p.post_id
+			 left join sys_user u on u.user_id = up.user_id
+		where u.user_name = #{userName}
+	</select>
+	
+	<select id="checkPostNameUnique" parameterType="String" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+		 where post_name=#{postName} limit 1
+	</select>
+	
+	<select id="checkPostCodeUnique" parameterType="String" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+		 where post_code=#{postCode} limit 1
+	</select>
+	
+	<update id="updatePost" parameterType="SysPost">
+ 		update sys_post
+ 		<set>
+ 			<if test="postCode != null and postCode != ''">post_code = #{postCode},</if>
+ 			<if test="postName != null and postName != ''">post_name = #{postName},</if>
+ 			<if test="postSort != null">post_sort = #{postSort},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where post_id = #{postId}
+	</update>
+ 	
+ 	<insert id="insertPost" parameterType="SysPost" useGeneratedKeys="true" keyProperty="postId">
+ 		insert into sys_post(
+ 			<if test="postId != null and postId != 0">post_id,</if>
+ 			<if test="postCode != null and postCode != ''">post_code,</if>
+ 			<if test="postName != null and postName != ''">post_name,</if>
+ 			<if test="postSort != null">post_sort,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="postId != null and postId != 0">#{postId},</if>
+ 			<if test="postCode != null and postCode != ''">#{postCode},</if>
+ 			<if test="postName != null and postName != ''">#{postName},</if>
+ 			<if test="postSort != null">#{postSort},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<delete id="deletePostById" parameterType="Long">
+		delete from sys_post where post_id = #{postId}
+	</delete>
+	
+	<delete id="deletePostByIds" parameterType="Long">
+ 		delete from sys_post where post_id in
+ 		<foreach collection="array" item="postId" open="(" separator="," close=")">
+ 			#{postId}
+        </foreach> 
+ 	</delete>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/pt-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml
new file mode 100644
index 0000000..7c4139b
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysRoleDeptMapper">
+
+	<resultMap type="SysRoleDept" id="SysRoleDeptResult">
+		<result property="roleId"     column="role_id"      />
+		<result property="deptId"     column="dept_id"      />
+	</resultMap>
+
+	<delete id="deleteRoleDeptByRoleId" parameterType="Long">
+		delete from sys_role_dept where role_id=#{roleId}
+	</delete>
+	
+	<select id="selectCountRoleDeptByDeptId" resultType="Integer">
+	    select count(1) from sys_role_dept where dept_id=#{deptId}
+	</select>
+	
+	<delete id="deleteRoleDept" parameterType="Long">
+ 		delete from sys_role_dept where role_id in
+ 		<foreach collection="array" item="roleId" open="(" separator="," close=")">
+ 			#{roleId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchRoleDept">
+		insert into sys_role_dept(role_id, dept_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.roleId},#{item.deptId})
+		</foreach>
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysRoleMapper.xml b/pt-system/src/main/resources/mapper/system/SysRoleMapper.xml
new file mode 100644
index 0000000..f0aa318
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysRoleMapper.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysRoleMapper">
+
+	<resultMap type="SysRole" id="SysRoleResult">
+		<id     property="roleId"             column="role_id"               />
+		<result property="roleName"           column="role_name"             />
+		<result property="roleKey"            column="role_key"              />
+		<result property="roleSort"           column="role_sort"             />
+		<result property="dataScope"          column="data_scope"            />
+		<result property="menuCheckStrictly"  column="menu_check_strictly"   />
+		<result property="deptCheckStrictly"  column="dept_check_strictly"   />
+		<result property="status"             column="status"                />
+		<result property="delFlag"            column="del_flag"              />
+		<result property="createBy"           column="create_by"             />
+		<result property="createTime"         column="create_time"           />
+		<result property="updateBy"           column="update_by"             />
+		<result property="updateTime"         column="update_time"           />
+		<result property="remark"             column="remark"                />
+	</resultMap>
+	
+	<sql id="selectRoleVo">
+	    select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,
+            r.status, r.del_flag, r.create_time, r.remark 
+        from sys_role r
+	        left join sys_user_role ur on ur.role_id = r.role_id
+	        left join sys_user u on u.user_id = ur.user_id
+	        left join sys_dept d on u.dept_id = d.dept_id
+    </sql>
+    
+    <select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		where r.del_flag = '0'
+		<if test="roleId != null and roleId != 0">
+			AND r.role_id = #{roleId}
+		</if>
+		<if test="roleName != null and roleName != ''">
+			AND r.role_name like concat('%', #{roleName}, '%')
+		</if>
+		<if test="status != null and status != ''">
+			AND r.status = #{status}
+		</if>
+		<if test="roleKey != null and roleKey != ''">
+			AND r.role_key like concat('%', #{roleKey}, '%')
+		</if>
+		<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+			and date_format(r.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+		</if>
+		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+			and date_format(r.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+		order by r.role_sort
+	</select>
+    
+	<select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		WHERE r.del_flag = '0' and ur.user_id = #{userId}
+	</select>
+	
+	<select id="selectRoleAll" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+	</select>
+	
+	<select id="selectRoleListByUserId" parameterType="Long" resultType="Long">
+		select r.role_id
+        from sys_role r
+	        left join sys_user_role ur on ur.role_id = r.role_id
+	        left join sys_user u on u.user_id = ur.user_id
+	    where u.user_id = #{userId}
+	</select>
+	
+	<select id="selectRoleById" parameterType="Long" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		where r.role_id = #{roleId}
+	</select>
+	
+	<select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		WHERE r.del_flag = '0' and u.user_name = #{userName}
+	</select>
+	
+	<select id="checkRoleNameUnique" parameterType="String" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		 where r.role_name=#{roleName} and r.del_flag = '0' limit 1
+	</select>
+	
+	<select id="checkRoleKeyUnique" parameterType="String" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		 where r.role_key=#{roleKey} and r.del_flag = '0' limit 1
+	</select>
+    <select id="page" resultType="com.ruoyi.system.object.vo.SysRolePageVO">
+		select role_id, role_name from sys_role where del_flag=0
+		<if test="name!=null and name!='' ">
+			and `role_name` like concat('%',#{name},'%')
+		</if>
+		order by role_sort asc
+	</select>
+
+
+	<insert id="insertRole" parameterType="SysRole" useGeneratedKeys="true" keyProperty="roleId">
+ 		insert into sys_role(
+ 			<if test="roleId != null and roleId != 0">role_id,</if>
+ 			<if test="roleName != null and roleName != ''">role_name,</if>
+ 			<if test="roleKey != null and roleKey != ''">role_key,</if>
+ 			<if test="roleSort != null">role_sort,</if>
+ 			<if test="dataScope != null and dataScope != ''">data_scope,</if>
+ 			<if test="menuCheckStrictly != null">menu_check_strictly,</if>
+ 			<if test="deptCheckStrictly != null">dept_check_strictly,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="roleId != null and roleId != 0">#{roleId},</if>
+ 			<if test="roleName != null and roleName != ''">#{roleName},</if>
+ 			<if test="roleKey != null and roleKey != ''">#{roleKey},</if>
+ 			<if test="roleSort != null">#{roleSort},</if>
+ 			<if test="dataScope != null and dataScope != ''">#{dataScope},</if>
+ 			<if test="menuCheckStrictly != null">#{menuCheckStrictly},</if>
+ 			<if test="deptCheckStrictly != null">#{deptCheckStrictly},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<update id="updateRole" parameterType="SysRole">
+ 		update sys_role
+ 		<set>
+ 			<if test="roleName != null and roleName != ''">role_name = #{roleName},</if>
+ 			<if test="roleKey != null and roleKey != ''">role_key = #{roleKey},</if>
+ 			<if test="roleSort != null">role_sort = #{roleSort},</if>
+ 			<if test="dataScope != null and dataScope != ''">data_scope = #{dataScope},</if>
+ 			<if test="menuCheckStrictly != null">menu_check_strictly = #{menuCheckStrictly},</if>
+ 			<if test="deptCheckStrictly != null">dept_check_strictly = #{deptCheckStrictly},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where role_id = #{roleId}
+	</update>
+	
+	<delete id="deleteRoleById" parameterType="Long">
+ 		update sys_role set del_flag = '2' where role_id = #{roleId}
+ 	</delete>
+ 	
+ 	<delete id="deleteRoleByIds" parameterType="Long">
+ 	    update sys_role set del_flag = '2' where role_id in
+ 		<foreach collection="array" item="roleId" open="(" separator="," close=")">
+ 			#{roleId}
+        </foreach> 
+ 	</delete>
+ 	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/pt-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml
new file mode 100644
index 0000000..3dc43fc
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysRoleMenuMapper">
+
+	<resultMap type="SysRoleMenu" id="SysRoleMenuResult">
+		<result property="roleId"     column="role_id"      />
+		<result property="menuId"     column="menu_id"      />
+	</resultMap>
+	
+	<select id="checkMenuExistRole" resultType="Integer">
+	    select count(1) from sys_role_menu where menu_id = #{menuId}
+	</select>
+    <select id="selectMenuIdsByRoleIds" resultType="java.lang.Long">
+		select menu_id from sys_role_menu where role_id in
+		<foreach collection="roleList" item="roleId" open="(" separator="," close=")">
+			#{roleId}
+		</foreach>
+	</select>
+
+    <delete id="deleteRoleMenuByRoleId" parameterType="Long">
+		delete from sys_role_menu where role_id=#{roleId}
+	</delete>
+	
+	<delete id="deleteRoleMenu" parameterType="Long">
+ 		delete from sys_role_menu where role_id in
+ 		<foreach collection="array" item="roleId" open="(" separator="," close=")">
+ 			#{roleId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchRoleMenu">
+		insert into sys_role_menu(role_id, menu_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.roleId},#{item.menuId})
+		</foreach>
+	</insert>
+	<insert id="insert">
+		INSERT INTO sys_role_menu (role_id, menu_id)
+		VALUES
+		<foreach collection="menuIds" item="menuId" separator=",">
+			(#{roleId}, #{menuId})
+		</foreach>
+	</insert>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysUserMapper.xml b/pt-system/src/main/resources/mapper/system/SysUserMapper.xml
new file mode 100644
index 0000000..30f57a4
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysUserMapper">
+
+    <resultMap type="SysUser" id="SysUserResult">
+        <id     property="userId"       column="user_id"      />
+        <result property="deptId"       column="dept_id"      />
+        <result property="userName"     column="user_name"    />
+        <result property="nickName"     column="nick_name"    />
+        <result property="email"        column="email"        />
+        <result property="phonenumber"  column="phonenumber"  />
+        <result property="sex"          column="sex"          />
+        <result property="avatar"       column="avatar"       />
+        <result property="password"     column="password"     />
+        <result property="status"       column="status"       />
+        <result property="delFlag"      column="del_flag"     />
+        <result property="loginIp"      column="login_ip"     />
+        <result property="loginDate"    column="login_date"   />
+        <result property="createBy"     column="create_by"    />
+        <result property="createTime"   column="create_time"  />
+        <result property="updateBy"     column="update_by"    />
+        <result property="updateTime"   column="update_time"  />
+        <result property="remark"       column="remark"       />
+        <association property="dept"    javaType="SysDept"         resultMap="deptResult" />
+        <collection  property="roles"   javaType="java.util.List"  resultMap="RoleResult" />
+    </resultMap>
+	
+    <resultMap id="deptResult" type="SysDept">
+        <id     property="deptId"    column="dept_id"     />
+        <result property="parentId"  column="parent_id"   />
+        <result property="deptName"  column="dept_name"   />
+        <result property="ancestors" column="ancestors"   />
+        <result property="orderNum"  column="order_num"   />
+        <result property="leader"    column="leader"      />
+        <result property="status"    column="dept_status" />
+    </resultMap>
+	
+    <resultMap id="RoleResult" type="SysRole">
+        <id     property="roleId"       column="role_id"        />
+        <result property="roleName"     column="role_name"      />
+        <result property="roleKey"      column="role_key"       />
+        <result property="roleSort"     column="role_sort"      />
+        <result property="dataScope"    column="data_scope"     />
+        <result property="status"       column="role_status"    />
+    </resultMap>
+	
+	<sql id="selectUserVo">
+        select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, 
+        d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
+        r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
+        from sys_user u
+		    left join sys_dept d on u.dept_id = d.dept_id
+		    left join sys_user_role ur on u.user_id = ur.user_id
+		    left join sys_role r on r.role_id = ur.role_id
+    </sql>
+    
+    <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
+		select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
+		left join sys_dept d on u.dept_id = d.dept_id
+		where u.del_flag = '0'
+		<if test="userId != null and userId != 0">
+			AND u.user_id = #{userId}
+		</if>
+		<if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="status != null and status != ''">
+			AND u.status = #{status}
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+			AND date_format(u.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+		</if>
+		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+			AND date_format(u.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+		</if>
+		<if test="deptId != null and deptId != 0">
+			AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
+	<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">
+	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
+	    from sys_user u
+			 left join sys_dept d on u.dept_id = d.dept_id
+			 left join sys_user_role ur on u.user_id = ur.user_id
+			 left join sys_role r on r.role_id = ur.role_id
+	    where u.del_flag = '0' and r.role_id = #{roleId}
+	    <if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
+	<select id="selectUnallocatedList" parameterType="SysUser" resultMap="SysUserResult">
+	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
+	    from sys_user u
+			 left join sys_dept d on u.dept_id = d.dept_id
+			 left join sys_user_role ur on u.user_id = ur.user_id
+			 left join sys_role r on r.role_id = ur.role_id
+	    where u.del_flag = '0' and (r.role_id != #{roleId} or r.role_id IS NULL)
+	    and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{roleId})
+	    <if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
+	<select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
+	    <include refid="selectUserVo"/>
+		where u.user_name = #{userName} and u.del_flag = '0'
+	</select>
+	
+	<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
+		<include refid="selectUserVo"/>
+		where u.user_id = #{userId}
+	</select>
+	
+	<select id="checkUserNameUnique" parameterType="String" resultMap="SysUserResult">
+		select user_id, user_name from sys_user where user_name = #{userName} and del_flag = '0' limit 1
+	</select>
+	
+	<select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult">
+		select user_id, phonenumber from sys_user where phonenumber = #{phonenumber} and del_flag = '0' limit 1
+	</select>
+	
+	<select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
+		select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
+	</select>
+    <select id="getSysUserPageList" resultType="com.ruoyi.system.object.vo.SysUserPageListVO">
+		select
+		su.user_id,
+		su.nick_name,
+		su.phonenumber,
+		sd.dept_id,
+		sd.dept_name,
+		sr.role_id,
+		sr.role_name,
+		su.status
+		from sys_user su
+
+		left join sys_dept sd on su.dept_id = sd.dept_id
+		left join sys_user_role sur on su.user_id = sur.user_id
+		left join sys_role sr on sur.role_id =sr.role_id
+		where
+		    su.del_flag='0'
+		<if test="nickName!=null and nickName!= '' ">
+			and su.nick_name like concat('%',#{nickName},'%')
+		</if>
+		<if test="phone!=null and phone!= '' ">
+			and su.phonenumber like concat('%',#{phone},'%')
+		</if>
+		<if test="status!=null and status!= '' ">
+			and su.status =#{status}
+		</if>
+	</select>
+	<select id="getSysUserVO" resultType="com.ruoyi.system.object.vo.SysUserVO">
+		select
+			su.user_id,
+			su.nick_name,
+			su.phonenumber,
+			sd.dept_id,
+			sd.dept_name,
+			sr.role_id,
+			sr.role_name,
+			su.status
+		from sys_user su
+				 left join sys_dept sd on su.dept_id = sd.dept_id
+				 left join sys_user_role sur on su.user_id = sur.user_id
+				 left join sys_role sr on sur.role_id =sr.role_id
+		where
+			su.del_flag='0'
+		and su.user_id=#{userId}
+	</select>
+	<select id="selectuserByPhone" resultType="com.ruoyi.common.core.domain.entity.SysUser">
+		select * from sys_user where del_flag='0' and phonenumber =#{phonenumber}
+	</select>
+
+	<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
+ 		insert into sys_user(
+ 			<if test="userId != null and userId != 0">user_id,</if>
+ 			<if test="deptId != null and deptId != 0">dept_id,</if>
+ 			<if test="userName != null and userName != ''">user_name,</if>
+ 			<if test="nickName != null and nickName != ''">nick_name,</if>
+ 			<if test="email != null and email != ''">email,</if>
+ 			<if test="avatar != null and avatar != ''">avatar,</if>
+ 			<if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
+ 			<if test="sex != null and sex != ''">sex,</if>
+ 			<if test="password != null and password != ''">password,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			create_time
+ 		)values(
+ 			<if test="userId != null and userId != ''">#{userId},</if>
+ 			<if test="deptId != null and deptId != ''">#{deptId},</if>
+ 			<if test="userName != null and userName != ''">#{userName},</if>
+ 			<if test="nickName != null and nickName != ''">#{nickName},</if>
+ 			<if test="email != null and email != ''">#{email},</if>
+ 			<if test="avatar != null and avatar != ''">#{avatar},</if>
+ 			<if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
+ 			<if test="sex != null and sex != ''">#{sex},</if>
+ 			<if test="password != null and password != ''">#{password},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<update id="updateUser" parameterType="SysUser">
+ 		update sys_user
+ 		<set>
+ 			<if test="deptId != null and deptId != 0">dept_id = #{deptId},</if>
+ 			<if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
+ 			<if test="email != null ">email = #{email},</if>
+ 			<if test="phonenumber != null ">phonenumber = #{phonenumber},</if>
+ 			<if test="sex != null and sex != ''">sex = #{sex},</if>
+ 			<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
+ 			<if test="password != null and password != ''">password = #{password},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
+ 			<if test="loginDate != null">login_date = #{loginDate},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where user_id = #{userId}
+	</update>
+	
+	<update id="updateUserStatus" parameterType="SysUser">
+ 		update sys_user set status = #{status} where user_id = #{userId}
+	</update>
+	
+	<update id="updateUserAvatar" parameterType="SysUser">
+ 		update sys_user set avatar = #{avatar} where user_name = #{userName}
+	</update>
+	
+	<update id="resetUserPwd" parameterType="SysUser">
+ 		update sys_user set password = #{password} where user_name = #{userName}
+	</update>
+	
+	<delete id="deleteUserById" parameterType="Long">
+ 		update sys_user set del_flag = '2' where user_id = #{userId}
+ 	</delete>
+ 	
+ 	<delete id="deleteUserByIds" parameterType="Long">
+ 		update sys_user set del_flag = '2' where user_id in
+ 		<foreach collection="array" item="userId" open="(" separator="," close=")">
+ 			#{userId}
+        </foreach> 
+ 	</delete>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/pt-system/src/main/resources/mapper/system/SysUserPostMapper.xml
new file mode 100644
index 0000000..2b90bc4
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysUserPostMapper.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysUserPostMapper">
+
+	<resultMap type="SysUserPost" id="SysUserPostResult">
+		<result property="userId"     column="user_id"      />
+		<result property="postId"     column="post_id"      />
+	</resultMap>
+
+	<delete id="deleteUserPostByUserId" parameterType="Long">
+		delete from sys_user_post where user_id=#{userId}
+	</delete>
+	
+	<select id="countUserPostById" resultType="Integer">
+	    select count(1) from sys_user_post where post_id=#{postId}  
+	</select>
+	
+	<delete id="deleteUserPost" parameterType="Long">
+ 		delete from sys_user_post where user_id in
+ 		<foreach collection="array" item="userId" open="(" separator="," close=")">
+ 			#{userId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchUserPost">
+		insert into sys_user_post(user_id, post_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.userId},#{item.postId})
+		</foreach>
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/pt-system/src/main/resources/mapper/system/SysUserRoleMapper.xml
new file mode 100644
index 0000000..8fadc85
--- /dev/null
+++ b/pt-system/src/main/resources/mapper/system/SysUserRoleMapper.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysUserRoleMapper">
+
+	<resultMap type="SysUserRole" id="SysUserRoleResult">
+		<result property="userId"     column="user_id"      />
+		<result property="roleId"     column="role_id"      />
+	</resultMap>
+
+	<delete id="deleteUserRoleByUserId" parameterType="Long">
+		delete from sys_user_role where user_id=#{userId}
+	</delete>
+	
+	<select id="countUserRoleByRoleId" resultType="Integer">
+	    select count(1) from sys_user_role where role_id=#{roleId}  
+	</select>
+	<select id="selectRoleByUserId" resultType="com.ruoyi.common.core.domain.entity.SysRole">
+		select * from sys_user_role where user_id=#{userId}
+	</select>
+
+	<delete id="deleteUserRole" parameterType="Long">
+ 		delete from sys_user_role where user_id in
+ 		<foreach collection="array" item="userId" open="(" separator="," close=")">
+ 			#{userId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchUserRole">
+		insert into sys_user_role(user_id, role_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.userId},#{item.roleId})
+		</foreach>
+	</insert>
+    <insert id="insert">
+		insert into sys_user_role(user_id, role_id) value
+			(#{userId},#{roleId})
+	</insert>
+
+    <delete id="deleteUserRoleInfo" parameterType="SysUserRole">
+		delete from sys_user_role where user_id=#{userId} and role_id=#{roleId}
+	</delete>
+	
+	<delete id="deleteUserRoleInfos">
+	    delete from sys_user_role where role_id=#{roleId} and user_id in
+ 	    <foreach collection="userIds" item="userId" open="(" separator="," close=")">
+ 	        #{userId}
+            </foreach> 
+	</delete>
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysConfigMapper.xml b/pt-system/target/classes/mapper/system/SysConfigMapper.xml
new file mode 100644
index 0000000..a5ff114
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysConfigMapper.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysConfigMapper">
+    
+    <resultMap type="SysConfig" id="SysConfigResult">
+    	<id     property="configId"      column="config_id"      />
+        <result property="configName"    column="config_name"    />
+        <result property="configKey"     column="config_key"     />
+        <result property="configValue"   column="config_value"   />
+        <result property="configType"    column="config_type"    />
+        <result property="createBy"      column="create_by"      />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"      column="update_by"      />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+    
+    <sql id="selectConfigVo">
+        select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark 
+		from sys_config
+    </sql>
+    
+    <!-- 查询条件 -->
+	<sql id="sqlwhereSearch">
+		<where>
+			<if test="configId !=null">
+				and config_id = #{configId}
+			</if>
+			<if test="configKey !=null and configKey != ''">
+				and config_key = #{configKey}
+			</if>
+		</where>
+	</sql>
+    
+    <select id="selectConfig" parameterType="SysConfig" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        <include refid="sqlwhereSearch"/>
+    </select>
+    
+    <select id="selectConfigList" parameterType="SysConfig" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        <where>
+			<if test="configName != null and configName != ''">
+				AND config_name like concat('%', #{configName}, '%')
+			</if>
+			<if test="configType != null and configType != ''">
+				AND config_type = #{configType}
+			</if>
+			<if test="configKey != null and configKey != ''">
+				AND config_key like concat('%', #{configKey}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+			</if>
+		</where>
+    </select>
+    
+    <select id="selectConfigById" parameterType="Long" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        where config_id = #{configId}
+    </select>
+	
+    <select id="checkConfigKeyUnique" parameterType="String" resultMap="SysConfigResult">
+        <include refid="selectConfigVo"/>
+        where config_key = #{configKey} limit 1
+    </select>
+    
+    <insert id="insertConfig" parameterType="SysConfig">
+        insert into sys_config (
+			<if test="configName != null and configName != '' ">config_name,</if>
+			<if test="configKey != null and configKey != '' ">config_key,</if>
+			<if test="configValue != null and configValue != '' ">config_value,</if>
+			<if test="configType != null and configType != '' ">config_type,</if>
+			<if test="createBy != null and createBy != ''">create_by,</if>
+			<if test="remark != null and remark != ''">remark,</if>
+ 			create_time
+        )values(
+			<if test="configName != null and configName != ''">#{configName},</if>
+			<if test="configKey != null and configKey != ''">#{configKey},</if>
+			<if test="configValue != null and configValue != ''">#{configValue},</if>
+			<if test="configType != null and configType != ''">#{configType},</if>
+			<if test="createBy != null and createBy != ''">#{createBy},</if>
+			<if test="remark != null and remark != ''">#{remark},</if>
+ 			sysdate()
+		)
+    </insert>
+	 
+    <update id="updateConfig" parameterType="SysConfig">
+        update sys_config 
+        <set>
+            <if test="configName != null and configName != ''">config_name = #{configName},</if>
+            <if test="configKey != null and configKey != ''">config_key = #{configKey},</if>
+            <if test="configValue != null and configValue != ''">config_value = #{configValue},</if>
+            <if test="configType != null and configType != ''">config_type = #{configType},</if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+ 			update_time = sysdate()
+        </set>
+        where config_id = #{configId}
+    </update>
+	
+    <delete id="deleteConfigById" parameterType="Long">
+        delete from sys_config where config_id = #{configId}
+    </delete>
+    
+    <delete id="deleteConfigByIds" parameterType="Long">
+        delete from sys_config where config_id in 
+        <foreach item="configId" collection="array" open="(" separator="," close=")">
+        	#{configId}
+        </foreach>
+    </delete>
+    
+</mapper>
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysDeptMapper.xml b/pt-system/target/classes/mapper/system/SysDeptMapper.xml
new file mode 100644
index 0000000..3120faa
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysDeptMapper.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysDeptMapper">
+
+	<resultMap type="SysDept" id="SysDeptResult">
+		<id     property="deptId"     column="dept_id"     />
+		<result property="parentId"   column="parent_id"   />
+		<result property="ancestors"  column="ancestors"   />
+		<result property="deptName"   column="dept_name"   />
+		<result property="orderNum"   column="order_num"   />
+		<result property="leader"     column="leader"      />
+		<result property="phone"      column="phone"       />
+		<result property="email"      column="email"       />
+		<result property="status"     column="status"      />
+		<result property="delFlag"    column="del_flag"    />
+		<result property="parentName" column="parent_name" />
+		<result property="createBy"   column="create_by"   />
+		<result property="createTime" column="create_time" />
+		<result property="updateBy"   column="update_by"   />
+		<result property="updateTime" column="update_time" />
+	</resultMap>
+	
+	<sql id="selectDeptVo">
+        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time 
+        from sys_dept d
+    </sql>
+    
+	<select id="selectDeptList" parameterType="SysDept" resultMap="SysDeptResult">
+        <include refid="selectDeptVo"/>
+        where d.del_flag = '0'
+		<if test="deptId != null and deptId != 0">
+			AND dept_id = #{deptId}
+		</if>
+        <if test="parentId != null and parentId != 0">
+			AND parent_id = #{parentId}
+		</if>
+		<if test="deptName != null and deptName != ''">
+			AND dept_name like concat('%', #{deptName}, '%')
+		</if>
+		<if test="status != null and status != ''">
+			AND status = #{status}
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+		order by d.parent_id, d.order_num
+    </select>
+    
+    <select id="selectDeptListByRoleId" resultType="Long">
+		select d.dept_id
+		from sys_dept d
+            left join sys_role_dept rd on d.dept_id = rd.dept_id
+        where rd.role_id = #{roleId}
+            <if test="deptCheckStrictly">
+              and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId})
+            </if>
+		order by d.parent_id, d.order_num
+	</select>
+    
+    <select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
+		select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,
+			(select dept_name from sys_dept where dept_id = d.parent_id) parent_name
+		from sys_dept d
+		where d.dept_id = #{deptId}
+	</select>
+    
+    <select id="checkDeptExistUser" parameterType="Long" resultType="int">
+		select count(1) from sys_user where dept_id = #{deptId} and del_flag = '0'
+	</select>
+	
+	<select id="hasChildByDeptId" parameterType="Long" resultType="int">
+		select count(1) from sys_dept
+		where del_flag = '0' and parent_id = #{deptId} limit 1
+	</select>
+	
+	<select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult">
+		select * from sys_dept where find_in_set(#{deptId}, ancestors)
+	</select>
+	
+	<select id="selectNormalChildrenDeptById" parameterType="Long" resultType="int">
+		select count(*) from sys_dept where status = 0 and del_flag = '0' and find_in_set(#{deptId}, ancestors)
+	</select>
+	
+	<select id="checkDeptNameUnique" resultMap="SysDeptResult">
+	    <include refid="selectDeptVo"/>
+		where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
+	</select>
+    <select id="page" resultType="com.ruoyi.system.object.vo.SysDeptPageVO">
+		select
+			dept_id,name
+		from sys_dept
+		where
+		    del_flag='0'
+	</select>
+	<select id="getDeptList" resultType="com.ruoyi.system.object.vo.SysDeptPageVO">
+		select
+			dept_id,name
+		from sys_dept
+		where
+			del_flag='0'
+
+	</select>
+	<select id="selectDeptByName" resultType="com.ruoyi.common.core.domain.entity.SysDept">
+		select dept_id from sys_dept where del_flag='0' and deptName like #{name}
+	</select>
+
+	<insert id="insertDept" parameterType="SysDept">
+ 		insert into sys_dept(
+ 			<if test="deptId != null and deptId != 0">dept_id,</if>
+ 			<if test="parentId != null and parentId != 0">parent_id,</if>
+ 			<if test="deptName != null and deptName != ''">dept_name,</if>
+ 			<if test="ancestors != null and ancestors != ''">ancestors,</if>
+ 			<if test="orderNum != null">order_num,</if>
+ 			<if test="leader != null and leader != ''">leader,</if>
+ 			<if test="phone != null and phone != ''">phone,</if>
+ 			<if test="email != null and email != ''">email,</if>
+ 			<if test="status != null">status,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="deptId != null and deptId != 0">#{deptId},</if>
+ 			<if test="parentId != null and parentId != 0">#{parentId},</if>
+ 			<if test="deptName != null and deptName != ''">#{deptName},</if>
+ 			<if test="ancestors != null and ancestors != ''">#{ancestors},</if>
+ 			<if test="orderNum != null">#{orderNum},</if>
+ 			<if test="leader != null and leader != ''">#{leader},</if>
+ 			<if test="phone != null and phone != ''">#{phone},</if>
+ 			<if test="email != null and email != ''">#{email},</if>
+ 			<if test="status != null">#{status},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<update id="updateDept" parameterType="SysDept">
+ 		update sys_dept
+ 		<set>
+ 			<if test="parentId != null and parentId != 0">parent_id = #{parentId},</if>
+ 			<if test="deptName != null and deptName != ''">dept_name = #{deptName},</if>
+ 			<if test="ancestors != null and ancestors != ''">ancestors = #{ancestors},</if>
+ 			<if test="orderNum != null">order_num = #{orderNum},</if>
+ 			<if test="leader != null">leader = #{leader},</if>
+ 			<if test="phone != null">phone = #{phone},</if>
+ 			<if test="email != null">email = #{email},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where dept_id = #{deptId}
+	</update>
+	
+	<update id="updateDeptChildren" parameterType="java.util.List">
+	    update sys_dept set ancestors =
+	    <foreach collection="depts" item="item" index="index"
+	        separator=" " open="case dept_id" close="end">
+	        when #{item.deptId} then #{item.ancestors}
+	    </foreach>
+	    where dept_id in
+	    <foreach collection="depts" item="item" index="index"
+	        separator="," open="(" close=")">
+	        #{item.deptId}
+	    </foreach>
+	</update>
+	 
+	<update id="updateDeptStatusNormal" parameterType="Long">
+ 	    update sys_dept set status = '0' where dept_id in 
+ 	    <foreach collection="array" item="deptId" open="(" separator="," close=")">
+        	#{deptId}
+        </foreach>
+	</update>
+	<update id="updateDeptName">
+		update sys_dept set dept_name = #{sysDept.deptName} where dept_id = #{sysDept.deptId}
+	</update>
+
+	<delete id="deleteDeptById" parameterType="Long">
+		update sys_dept set del_flag = '2' where dept_id = #{deptId}
+	</delete>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysDictDataMapper.xml b/pt-system/target/classes/mapper/system/SysDictDataMapper.xml
new file mode 100644
index 0000000..3b94b7f
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysDictDataMapper.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysDictDataMapper">
+	
+	<resultMap type="SysDictData" id="SysDictDataResult">
+		<id     property="dictCode"   column="dict_code"   />
+		<result property="dictSort"   column="dict_sort"   />
+		<result property="dictLabel"  column="dict_label"  />
+		<result property="dictValue"  column="dict_value"  />
+		<result property="dictType"   column="dict_type"   />
+		<result property="cssClass"   column="css_class"   />
+		<result property="listClass"  column="list_class"  />
+		<result property="isDefault"  column="is_default"  />
+		<result property="status"     column="status"      />
+		<result property="createBy"   column="create_by"   />
+		<result property="createTime" column="create_time" />
+		<result property="updateBy"   column="update_by"   />
+		<result property="updateTime" column="update_time" />
+	</resultMap>
+	
+	<sql id="selectDictDataVo">
+        select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark 
+		from sys_dict_data
+    </sql>
+
+	<select id="selectDictDataList" parameterType="SysDictData" resultMap="SysDictDataResult">
+	    <include refid="selectDictDataVo"/>
+		<where>
+		    <if test="dictType != null and dictType != ''">
+				AND dict_type = #{dictType}
+			</if>
+			<if test="dictLabel != null and dictLabel != ''">
+				AND dict_label like concat('%', #{dictLabel}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+		</where>
+		order by dict_sort asc
+	</select>
+	
+	<select id="selectDictDataByType" parameterType="String" resultMap="SysDictDataResult">
+		<include refid="selectDictDataVo"/>
+		where status = '0' and dict_type = #{dictType} order by dict_sort asc
+	</select>
+	
+	<select id="selectDictLabel" resultType="String">
+		select dict_label from sys_dict_data
+		where dict_type = #{dictType} and dict_value = #{dictValue}
+	</select>
+	
+	<select id="selectDictDataById" parameterType="Long" resultMap="SysDictDataResult">
+		<include refid="selectDictDataVo"/>
+		where dict_code = #{dictCode}
+	</select>
+	
+	<select id="countDictDataByType" resultType="Integer">
+	    select count(1) from sys_dict_data where dict_type=#{dictType}  
+	</select>
+	
+	<delete id="deleteDictDataById" parameterType="Long">
+ 		delete from sys_dict_data where dict_code = #{dictCode}
+ 	</delete>
+ 	
+ 	<delete id="deleteDictDataByIds" parameterType="Long">
+ 		delete from sys_dict_data where dict_code in
+ 		<foreach collection="array" item="dictCode" open="(" separator="," close=")">
+ 			#{dictCode}
+        </foreach> 
+ 	</delete>
+	
+	<update id="updateDictData" parameterType="SysDictData">
+ 		update sys_dict_data
+ 		<set>
+ 			<if test="dictSort != null">dict_sort = #{dictSort},</if>
+ 			<if test="dictLabel != null and dictLabel != ''">dict_label = #{dictLabel},</if>
+ 			<if test="dictValue != null and dictValue != ''">dict_value = #{dictValue},</if>
+ 			<if test="dictType != null and dictType != ''">dict_type = #{dictType},</if>
+ 			<if test="cssClass != null">css_class = #{cssClass},</if>
+ 			<if test="listClass != null">list_class = #{listClass},</if>
+ 			<if test="isDefault != null and isDefault != ''">is_default = #{isDefault},</if>
+ 			<if test="status != null">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where dict_code = #{dictCode}
+	</update>
+	
+	<update id="updateDictDataType" parameterType="String">
+ 		update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType}
+	</update>
+ 	
+ 	<insert id="insertDictData" parameterType="SysDictData">
+ 		insert into sys_dict_data(
+ 			<if test="dictSort != null">dict_sort,</if>
+ 			<if test="dictLabel != null and dictLabel != ''">dict_label,</if>
+ 			<if test="dictValue != null and dictValue != ''">dict_value,</if>
+ 			<if test="dictType != null and dictType != ''">dict_type,</if>
+ 			<if test="cssClass != null and cssClass != ''">css_class,</if>
+ 			<if test="listClass != null and listClass != ''">list_class,</if>
+ 			<if test="isDefault != null and isDefault != ''">is_default,</if>
+ 			<if test="status != null">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 		    <if test="dictSort != null">#{dictSort},</if>
+ 		    <if test="dictLabel != null and dictLabel != ''">#{dictLabel},</if>
+ 			<if test="dictValue != null and dictValue != ''">#{dictValue},</if>
+ 			<if test="dictType != null and dictType != ''">#{dictType},</if>
+ 			<if test="cssClass != null and cssClass != ''">#{cssClass},</if>
+ 			<if test="listClass != null and listClass != ''">#{listClass},</if>
+ 			<if test="isDefault != null and isDefault != ''">#{isDefault},</if>
+ 			<if test="status != null">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysDictTypeMapper.xml b/pt-system/target/classes/mapper/system/SysDictTypeMapper.xml
new file mode 100644
index 0000000..438d484
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysDictTypeMapper.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysDictTypeMapper">
+
+	<resultMap type="SysDictType" id="SysDictTypeResult">
+		<id     property="dictId"     column="dict_id"     />
+		<result property="dictName"   column="dict_name"   />
+		<result property="dictType"   column="dict_type"   />
+		<result property="status"     column="status"      />
+		<result property="createBy"   column="create_by"   />
+		<result property="createTime" column="create_time" />
+		<result property="updateBy"   column="update_by"   />
+		<result property="updateTime" column="update_time" />
+	</resultMap>
+	
+	<sql id="selectDictTypeVo">
+        select dict_id, dict_name, dict_type, status, create_by, create_time, remark 
+		from sys_dict_type
+    </sql>
+
+	<select id="selectDictTypeList" parameterType="SysDictType" resultMap="SysDictTypeResult">
+	    <include refid="selectDictTypeVo"/>
+		<where>
+		    <if test="dictName != null and dictName != ''">
+				AND dict_name like concat('%', #{dictName}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="dictType != null and dictType != ''">
+				AND dict_type like concat('%', #{dictType}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				and date_format(create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+			</if>
+	    </where>
+	</select>
+	
+	<select id="selectDictTypeAll" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+	</select>
+	
+	<select id="selectDictTypeById" parameterType="Long" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+		where dict_id = #{dictId}
+	</select>
+	
+	<select id="selectDictTypeByType" parameterType="String" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+		where dict_type = #{dictType}
+	</select>
+	
+	<select id="checkDictTypeUnique" parameterType="String" resultMap="SysDictTypeResult">
+		<include refid="selectDictTypeVo"/>
+		where dict_type = #{dictType} limit 1
+	</select>
+	
+	<delete id="deleteDictTypeById" parameterType="Long">
+ 		delete from sys_dict_type where dict_id = #{dictId}
+ 	</delete>
+ 	
+ 	<delete id="deleteDictTypeByIds" parameterType="Long">
+ 		delete from sys_dict_type where dict_id in
+ 		<foreach collection="array" item="dictId" open="(" separator="," close=")">
+ 			#{dictId}
+        </foreach> 
+ 	</delete>
+
+ 	<update id="updateDictType" parameterType="SysDictType">
+ 		update sys_dict_type
+ 		<set>
+ 			<if test="dictName != null and dictName != ''">dict_name = #{dictName},</if>
+ 			<if test="dictType != null and dictType != ''">dict_type = #{dictType},</if>
+ 			<if test="status != null">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where dict_id = #{dictId}
+	</update>
+ 	
+ 	<insert id="insertDictType" parameterType="SysDictType">
+ 		insert into sys_dict_type(
+ 			<if test="dictName != null and dictName != ''">dict_name,</if>
+ 			<if test="dictType != null and dictType != ''">dict_type,</if>
+ 			<if test="status != null">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="dictName != null and dictName != ''">#{dictName},</if>
+ 			<if test="dictType != null and dictType != ''">#{dictType},</if>
+ 			<if test="status != null">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysLogininforMapper.xml b/pt-system/target/classes/mapper/system/SysLogininforMapper.xml
new file mode 100644
index 0000000..822d665
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysLogininforMapper.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysLogininforMapper">
+
+	<resultMap type="SysLogininfor" id="SysLogininforResult">
+		<id     property="infoId"        column="info_id"           />
+		<result property="userName"      column="user_name"         />
+		<result property="status"        column="status"            />
+		<result property="ipaddr"        column="ipaddr"            />
+		<result property="loginLocation" column="login_location"    />
+		<result property="browser"       column="browser"           />
+		<result property="os"            column="os"                />
+		<result property="msg"           column="msg"               />
+		<result property="loginTime"     column="login_time"        />
+	</resultMap>
+
+	<insert id="insertLogininfor" parameterType="SysLogininfor">
+		insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time)
+		values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate())
+	</insert>
+	
+	<select id="selectLogininforList" parameterType="SysLogininfor" resultMap="SysLogininforResult">
+		select info_id, user_name, ipaddr, login_location, browser, os, status, msg, login_time from sys_logininfor
+		<where>
+			<if test="ipaddr != null and ipaddr != ''">
+				AND ipaddr like concat('%', #{ipaddr}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="userName != null and userName != ''">
+				AND user_name like concat('%', #{userName}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				AND login_time &gt;= #{params.beginTime}
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				AND login_time &lt;= #{params.endTime}
+			</if>
+		</where>
+		order by info_id desc
+	</select>
+	
+	<delete id="deleteLogininforByIds" parameterType="Long">
+ 		delete from sys_logininfor where info_id in
+ 		<foreach collection="array" item="infoId" open="(" separator="," close=")">
+ 			#{infoId}
+        </foreach> 
+ 	</delete>
+    
+    <update id="cleanLogininfor">
+        truncate table sys_logininfor
+    </update>
+    
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysMenuMapper.xml b/pt-system/target/classes/mapper/system/SysMenuMapper.xml
new file mode 100644
index 0000000..c063570
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysMenuMapper.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+		PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+		"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysMenuMapper">
+
+	<resultMap type="SysMenu" id="SysMenuResult">
+		<id     property="menuId"         column="menu_id"        />
+		<result property="menuName"       column="menu_name"      />
+		<result property="parentName"     column="parent_name"    />
+		<result property="parentId"       column="parent_id"      />
+		<result property="orderNum"       column="order_num"      />
+		<result property="path"           column="path"           />
+		<result property="component"      column="component"      />
+		<result property="query"          column="query"          />
+		<result property="routeName"      column="route_name"     />
+		<result property="isFrame"        column="is_frame"       />
+		<result property="isCache"        column="is_cache"       />
+		<result property="menuType"       column="menu_type"      />
+		<result property="visible"        column="visible"        />
+		<result property="status"         column="status"         />
+		<result property="perms"          column="perms"          />
+		<result property="icon"           column="icon"           />
+		<result property="createBy"       column="create_by"      />
+		<result property="createTime"     column="create_time"    />
+		<result property="updateTime"     column="update_time"    />
+		<result property="updateBy"       column="update_by"      />
+		<result property="remark"         column="remark"         />
+	</resultMap>
+
+	<sql id="selectMenuVo">
+        select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time 
+		from sys_menu
+    </sql>
+    
+    <select id="selectMenuList" parameterType="SysMenu" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		<where>
+			<if test="menuName != null and menuName != ''">
+				AND menu_name like concat('%', #{menuName}, '%')
+			</if>
+			<if test="visible != null and visible != ''">
+				AND visible = #{visible}
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+		</where>
+		order by parent_id, order_num
+	</select>
+	
+	<select id="selectMenuTreeAll" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		from sys_menu m where m.menu_type in ('M', 'C') and m.status = 0
+		order by m.parent_id, m.order_num
+	</select>
+	
+	<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		from sys_menu m
+		left join sys_role_menu rm on m.menu_id = rm.menu_id
+		left join sys_user_role ur on rm.role_id = ur.role_id
+		left join sys_role ro on ur.role_id = ro.role_id
+		where ur.user_id = #{params.userId}
+		<if test="menuName != null and menuName != ''">
+            AND m.menu_name like concat('%', #{menuName}, '%')
+		</if>
+		<if test="visible != null and visible != ''">
+            AND m.visible = #{visible}
+		</if>
+		<if test="status != null and status != ''">
+            AND m.status = #{status}
+		</if>
+		order by m.parent_id, m.order_num
+	</select>
+    
+    <select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
+		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+			 left join sys_user_role ur on rm.role_id = ur.role_id
+			 left join sys_role ro on ur.role_id = ro.role_id
+			 left join sys_user u on ur.user_id = u.user_id
+		where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = 0  AND ro.status = 0
+		order by m.parent_id, m.order_num
+	</select>
+	
+	<select id="selectMenuListByRoleId" resultType="Long">
+		select m.menu_id
+		from sys_menu m
+            left join sys_role_menu rm on m.menu_id = rm.menu_id
+        where rm.role_id = #{roleId}
+            <if test="menuCheckStrictly">
+              and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = #{roleId})
+            </if>
+		order by m.parent_id, m.order_num
+	</select>
+	
+	<select id="selectMenuPerms" resultType="String">
+		select distinct m.perms
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+			 left join sys_user_role ur on rm.role_id = ur.role_id
+	</select>
+
+	<select id="selectMenuPermsByUserId" parameterType="Long" resultType="String">
+		select distinct m.perms
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+			 left join sys_user_role ur on rm.role_id = ur.role_id
+			 left join sys_role r on r.role_id = ur.role_id
+		where m.status = '0' and r.status = '0' and ur.user_id = #{userId}
+	</select>
+	
+	<select id="selectMenuPermsByRoleId" parameterType="Long" resultType="String">
+		select distinct m.perms
+		from sys_menu m
+			 left join sys_role_menu rm on m.menu_id = rm.menu_id
+		where m.status = '0' and rm.role_id = #{roleId}
+	</select>
+	
+	<select id="selectMenuById" parameterType="Long" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		where menu_id = #{menuId}
+	</select>
+	
+	<select id="hasChildByMenuId" resultType="Integer">
+	    select count(1) from sys_menu where parent_id = #{menuId}  
+	</select>
+	
+	<select id="checkMenuNameUnique" parameterType="SysMenu" resultMap="SysMenuResult">
+		<include refid="selectMenuVo"/>
+		where menu_name=#{menuName} and parent_id = #{parentId} limit 1
+	</select>
+
+	<select id="getAllRootMenu" resultType="com.ruoyi.system.object.vo.MenuTreeVO">
+		select distinct sm.menu_id,sm.menu_name,sm.path,sm.menu_type
+		from  sys_menu  sm
+		where
+		sm.parent_id = 0 order by sm.order_num 	asc
+	</select>
+
+	<select id="selectMenusByParentId" resultType="com.ruoyi.system.object.vo.MenuTreeVO">
+		select menu_id,menu_name,path,menu_type from sys_menu
+		where parent_id = #{menuId} order by order_num 	asc
+	</select>
+
+	<update id="updateMenu" parameterType="SysMenu">
+		update sys_menu
+		<set>
+			<if test="menuName != null and menuName != ''">menu_name = #{menuName},</if>
+			<if test="parentId != null">parent_id = #{parentId},</if>
+			<if test="orderNum != null">order_num = #{orderNum},</if>
+			<if test="path != null and path != ''">path = #{path},</if>
+			<if test="component != null">component = #{component},</if>
+			<if test="query != null">`query` = #{query},</if>
+			<if test="routeName != null">route_name = #{routeName},</if>
+			<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
+			<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
+			<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>
+			<if test="visible != null">visible = #{visible},</if>
+			<if test="status != null">status = #{status},</if>
+			<if test="perms !=null">perms = #{perms},</if>
+			<if test="icon !=null and icon != ''">icon = #{icon},</if>
+			<if test="remark != null and remark != ''">remark = #{remark},</if>
+			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+			update_time = sysdate()
+		</set>
+		where menu_id = #{menuId}
+	</update>
+
+	<insert id="insertMenu" parameterType="SysMenu">
+		insert into sys_menu(
+		<if test="menuId != null and menuId != 0">menu_id,</if>
+		<if test="parentId != null and parentId != 0">parent_id,</if>
+		<if test="menuName != null and menuName != ''">menu_name,</if>
+		<if test="orderNum != null">order_num,</if>
+		<if test="path != null and path != ''">path,</if>
+		<if test="component != null and component != ''">component,</if>
+		<if test="query != null and query != ''">`query`,</if>
+		<if test="routeName != null">route_name,</if>
+		<if test="isFrame != null and isFrame != ''">is_frame,</if>
+		<if test="isCache != null and isCache != ''">is_cache,</if>
+		<if test="menuType != null and menuType != ''">menu_type,</if>
+		<if test="visible != null">visible,</if>
+		<if test="status != null">status,</if>
+		<if test="perms !=null and perms != ''">perms,</if>
+		<if test="icon != null and icon != ''">icon,</if>
+		<if test="remark != null and remark != ''">remark,</if>
+		<if test="createBy != null and createBy != ''">create_by,</if>
+		create_time
+		)values(
+		<if test="menuId != null and menuId != 0">#{menuId},</if>
+		<if test="parentId != null and parentId != 0">#{parentId},</if>
+		<if test="menuName != null and menuName != ''">#{menuName},</if>
+		<if test="orderNum != null">#{orderNum},</if>
+		<if test="path != null and path != ''">#{path},</if>
+		<if test="component != null and component != ''">#{component},</if>
+		<if test="query != null and query != ''">#{query},</if>
+		<if test="routeName != null">#{routeName},</if>
+		<if test="isFrame != null and isFrame != ''">#{isFrame},</if>
+		<if test="isCache != null and isCache != ''">#{isCache},</if>
+		<if test="menuType != null and menuType != ''">#{menuType},</if>
+		<if test="visible != null">#{visible},</if>
+		<if test="status != null">#{status},</if>
+		<if test="perms !=null and perms != ''">#{perms},</if>
+		<if test="icon != null and icon != ''">#{icon},</if>
+		<if test="remark != null and remark != ''">#{remark},</if>
+		<if test="createBy != null and createBy != ''">#{createBy},</if>
+		sysdate()
+		)
+	</insert>
+	
+	<delete id="deleteMenuById" parameterType="Long">
+	    delete from sys_menu where menu_id = #{menuId}
+	</delete>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysNoticeMapper.xml b/pt-system/target/classes/mapper/system/SysNoticeMapper.xml
new file mode 100644
index 0000000..65d3079
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysNoticeMapper.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysNoticeMapper">
+    
+    <resultMap type="SysNotice" id="SysNoticeResult">
+        <result property="noticeId"       column="notice_id"       />
+        <result property="noticeTitle"    column="notice_title"    />
+        <result property="noticeType"     column="notice_type"     />
+        <result property="noticeContent"  column="notice_content"  />
+        <result property="status"         column="status"          />
+        <result property="createBy"       column="create_by"       />
+        <result property="createTime"     column="create_time"     />
+        <result property="updateBy"       column="update_by"       />
+        <result property="updateTime"     column="update_time"     />
+        <result property="remark"         column="remark"          />
+    </resultMap>
+    
+    <sql id="selectNoticeVo">
+        select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark 
+		from sys_notice
+    </sql>
+    
+    <select id="selectNoticeById" parameterType="Long" resultMap="SysNoticeResult">
+        <include refid="selectNoticeVo"/>
+        where notice_id = #{noticeId}
+    </select>
+    
+    <select id="selectNoticeList" parameterType="SysNotice" resultMap="SysNoticeResult">
+        <include refid="selectNoticeVo"/>
+        <where>
+			<if test="noticeTitle != null and noticeTitle != ''">
+				AND notice_title like concat('%', #{noticeTitle}, '%')
+			</if>
+			<if test="noticeType != null and noticeType != ''">
+				AND notice_type = #{noticeType}
+			</if>
+			<if test="createBy != null and createBy != ''">
+				AND create_by like concat('%', #{createBy}, '%')
+			</if>
+		</where>
+    </select>
+    
+    <insert id="insertNotice" parameterType="SysNotice">
+        insert into sys_notice (
+			<if test="noticeTitle != null and noticeTitle != '' ">notice_title, </if>
+			<if test="noticeType != null and noticeType != '' ">notice_type, </if>
+			<if test="noticeContent != null and noticeContent != '' ">notice_content, </if>
+			<if test="status != null and status != '' ">status, </if>
+			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+			<if test="noticeTitle != null and noticeTitle != ''">#{noticeTitle}, </if>
+			<if test="noticeType != null and noticeType != ''">#{noticeType}, </if>
+			<if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if>
+			<if test="status != null and status != ''">#{status}, </if>
+			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+		)
+    </insert>
+	 
+    <update id="updateNotice" parameterType="SysNotice">
+        update sys_notice 
+        <set>
+            <if test="noticeTitle != null and noticeTitle != ''">notice_title = #{noticeTitle}, </if>
+            <if test="noticeType != null and noticeType != ''">notice_type = #{noticeType}, </if>
+            <if test="noticeContent != null">notice_content = #{noticeContent}, </if>
+            <if test="status != null and status != ''">status = #{status}, </if>
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+        </set>
+        where notice_id = #{noticeId}
+    </update>
+	
+    <delete id="deleteNoticeById" parameterType="Long">
+        delete from sys_notice where notice_id = #{noticeId}
+    </delete>
+    
+    <delete id="deleteNoticeByIds" parameterType="Long">
+        delete from sys_notice where notice_id in 
+        <foreach item="noticeId" collection="array" open="(" separator="," close=")">
+            #{noticeId}
+        </foreach>
+    </delete>
+    
+</mapper>
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysOperLogMapper.xml b/pt-system/target/classes/mapper/system/SysOperLogMapper.xml
new file mode 100644
index 0000000..201db07
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysOperLogMapper.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysOperLogMapper">
+
+	<resultMap type="SysOperLog" id="SysOperLogResult">
+		<id     property="operId"         column="oper_id"        />
+		<result property="title"          column="title"          />
+		<result property="businessType"   column="business_type"  />
+		<result property="method"         column="method"         />
+		<result property="requestMethod"  column="request_method" />
+		<result property="operatorType"   column="operator_type"  />
+		<result property="operName"       column="oper_name"      />
+		<result property="deptName"       column="dept_name"      />
+		<result property="operUrl"        column="oper_url"       />
+		<result property="operIp"         column="oper_ip"        />
+		<result property="operLocation"   column="oper_location"  />
+		<result property="operParam"      column="oper_param"     />
+		<result property="jsonResult"     column="json_result"    />
+		<result property="status"         column="status"         />
+		<result property="errorMsg"       column="error_msg"      />
+		<result property="operTime"       column="oper_time"      />
+		<result property="costTime"       column="cost_time"      />
+	</resultMap>
+
+	<sql id="selectOperLogVo">
+        select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time
+        from sys_oper_log
+    </sql>
+    
+	<insert id="insertOperlog" parameterType="SysOperLog">
+		insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time)
+        values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate())
+	</insert>
+	
+	<select id="selectOperLogList" parameterType="SysOperLog" resultMap="SysOperLogResult">
+		<include refid="selectOperLogVo"/>
+		<where>
+			<if test="operIp != null and operIp != ''">
+				AND oper_ip like concat('%', #{operIp}, '%')
+			</if>
+			<if test="title != null and title != ''">
+				AND title like concat('%', #{title}, '%')
+			</if>
+			<if test="businessType != null">
+				AND business_type = #{businessType}
+			</if>
+			<if test="businessTypes != null and businessTypes.length > 0">
+			    AND business_type in
+			    <foreach collection="businessTypes" item="businessType" open="(" separator="," close=")">
+		 			#{businessType}
+		        </foreach> 
+			</if>
+			<if test="status != null">
+				AND status = #{status}
+			</if>
+			<if test="operName != null and operName != ''">
+				AND oper_name like concat('%', #{operName}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+				AND oper_time &gt;= #{params.beginTime}
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+				AND oper_time &lt;= #{params.endTime}
+			</if>
+		</where>
+		order by oper_id desc
+	</select>
+	
+	<delete id="deleteOperLogByIds" parameterType="Long">
+ 		delete from sys_oper_log where oper_id in
+ 		<foreach collection="array" item="operId" open="(" separator="," close=")">
+ 			#{operId}
+        </foreach> 
+ 	</delete>
+ 	
+ 	<select id="selectOperLogById" parameterType="Long" resultMap="SysOperLogResult">
+		<include refid="selectOperLogVo"/>
+		where oper_id = #{operId}
+	</select>
+	
+	<update id="cleanOperLog">
+        truncate table sys_oper_log
+    </update>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysPostMapper.xml b/pt-system/target/classes/mapper/system/SysPostMapper.xml
new file mode 100644
index 0000000..227c459
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysPostMapper.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysPostMapper">
+
+	<resultMap type="SysPost" id="SysPostResult">
+		<id     property="postId"        column="post_id"       />
+		<result property="postCode"      column="post_code"     />
+		<result property="postName"      column="post_name"     />
+		<result property="postSort"      column="post_sort"     />
+		<result property="status"        column="status"        />
+		<result property="createBy"      column="create_by"     />
+		<result property="createTime"    column="create_time"   />
+		<result property="updateBy"      column="update_by"     />
+		<result property="updateTime"    column="update_time"   />
+		<result property="remark"        column="remark"        />
+	</resultMap>
+	
+	<sql id="selectPostVo">
+        select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark 
+		from sys_post
+    </sql>
+	
+	<select id="selectPostList" parameterType="SysPost" resultMap="SysPostResult">
+	    <include refid="selectPostVo"/>
+		<where>
+			<if test="postCode != null and postCode != ''">
+				AND post_code like concat('%', #{postCode}, '%')
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="postName != null and postName != ''">
+				AND post_name like concat('%', #{postName}, '%')
+			</if>
+		</where>
+	</select>
+	
+	<select id="selectPostAll" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+	</select>
+	
+	<select id="selectPostById" parameterType="Long" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+		where post_id = #{postId}
+	</select>
+	
+	<select id="selectPostListByUserId" parameterType="Long" resultType="Long">
+		select p.post_id
+        from sys_post p
+	        left join sys_user_post up on up.post_id = p.post_id
+	        left join sys_user u on u.user_id = up.user_id
+	    where u.user_id = #{userId}
+	</select>
+	
+	<select id="selectPostsByUserName" parameterType="String" resultMap="SysPostResult">
+		select p.post_id, p.post_name, p.post_code
+		from sys_post p
+			 left join sys_user_post up on up.post_id = p.post_id
+			 left join sys_user u on u.user_id = up.user_id
+		where u.user_name = #{userName}
+	</select>
+	
+	<select id="checkPostNameUnique" parameterType="String" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+		 where post_name=#{postName} limit 1
+	</select>
+	
+	<select id="checkPostCodeUnique" parameterType="String" resultMap="SysPostResult">
+		<include refid="selectPostVo"/>
+		 where post_code=#{postCode} limit 1
+	</select>
+	
+	<update id="updatePost" parameterType="SysPost">
+ 		update sys_post
+ 		<set>
+ 			<if test="postCode != null and postCode != ''">post_code = #{postCode},</if>
+ 			<if test="postName != null and postName != ''">post_name = #{postName},</if>
+ 			<if test="postSort != null">post_sort = #{postSort},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where post_id = #{postId}
+	</update>
+ 	
+ 	<insert id="insertPost" parameterType="SysPost" useGeneratedKeys="true" keyProperty="postId">
+ 		insert into sys_post(
+ 			<if test="postId != null and postId != 0">post_id,</if>
+ 			<if test="postCode != null and postCode != ''">post_code,</if>
+ 			<if test="postName != null and postName != ''">post_name,</if>
+ 			<if test="postSort != null">post_sort,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="postId != null and postId != 0">#{postId},</if>
+ 			<if test="postCode != null and postCode != ''">#{postCode},</if>
+ 			<if test="postName != null and postName != ''">#{postName},</if>
+ 			<if test="postSort != null">#{postSort},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<delete id="deletePostById" parameterType="Long">
+		delete from sys_post where post_id = #{postId}
+	</delete>
+	
+	<delete id="deletePostByIds" parameterType="Long">
+ 		delete from sys_post where post_id in
+ 		<foreach collection="array" item="postId" open="(" separator="," close=")">
+ 			#{postId}
+        </foreach> 
+ 	</delete>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysRoleDeptMapper.xml b/pt-system/target/classes/mapper/system/SysRoleDeptMapper.xml
new file mode 100644
index 0000000..7c4139b
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysRoleDeptMapper.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysRoleDeptMapper">
+
+	<resultMap type="SysRoleDept" id="SysRoleDeptResult">
+		<result property="roleId"     column="role_id"      />
+		<result property="deptId"     column="dept_id"      />
+	</resultMap>
+
+	<delete id="deleteRoleDeptByRoleId" parameterType="Long">
+		delete from sys_role_dept where role_id=#{roleId}
+	</delete>
+	
+	<select id="selectCountRoleDeptByDeptId" resultType="Integer">
+	    select count(1) from sys_role_dept where dept_id=#{deptId}
+	</select>
+	
+	<delete id="deleteRoleDept" parameterType="Long">
+ 		delete from sys_role_dept where role_id in
+ 		<foreach collection="array" item="roleId" open="(" separator="," close=")">
+ 			#{roleId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchRoleDept">
+		insert into sys_role_dept(role_id, dept_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.roleId},#{item.deptId})
+		</foreach>
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysRoleMapper.xml b/pt-system/target/classes/mapper/system/SysRoleMapper.xml
new file mode 100644
index 0000000..f0aa318
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysRoleMapper.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysRoleMapper">
+
+	<resultMap type="SysRole" id="SysRoleResult">
+		<id     property="roleId"             column="role_id"               />
+		<result property="roleName"           column="role_name"             />
+		<result property="roleKey"            column="role_key"              />
+		<result property="roleSort"           column="role_sort"             />
+		<result property="dataScope"          column="data_scope"            />
+		<result property="menuCheckStrictly"  column="menu_check_strictly"   />
+		<result property="deptCheckStrictly"  column="dept_check_strictly"   />
+		<result property="status"             column="status"                />
+		<result property="delFlag"            column="del_flag"              />
+		<result property="createBy"           column="create_by"             />
+		<result property="createTime"         column="create_time"           />
+		<result property="updateBy"           column="update_by"             />
+		<result property="updateTime"         column="update_time"           />
+		<result property="remark"             column="remark"                />
+	</resultMap>
+	
+	<sql id="selectRoleVo">
+	    select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,
+            r.status, r.del_flag, r.create_time, r.remark 
+        from sys_role r
+	        left join sys_user_role ur on ur.role_id = r.role_id
+	        left join sys_user u on u.user_id = ur.user_id
+	        left join sys_dept d on u.dept_id = d.dept_id
+    </sql>
+    
+    <select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		where r.del_flag = '0'
+		<if test="roleId != null and roleId != 0">
+			AND r.role_id = #{roleId}
+		</if>
+		<if test="roleName != null and roleName != ''">
+			AND r.role_name like concat('%', #{roleName}, '%')
+		</if>
+		<if test="status != null and status != ''">
+			AND r.status = #{status}
+		</if>
+		<if test="roleKey != null and roleKey != ''">
+			AND r.role_key like concat('%', #{roleKey}, '%')
+		</if>
+		<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+			and date_format(r.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+		</if>
+		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+			and date_format(r.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+		order by r.role_sort
+	</select>
+    
+	<select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		WHERE r.del_flag = '0' and ur.user_id = #{userId}
+	</select>
+	
+	<select id="selectRoleAll" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+	</select>
+	
+	<select id="selectRoleListByUserId" parameterType="Long" resultType="Long">
+		select r.role_id
+        from sys_role r
+	        left join sys_user_role ur on ur.role_id = r.role_id
+	        left join sys_user u on u.user_id = ur.user_id
+	    where u.user_id = #{userId}
+	</select>
+	
+	<select id="selectRoleById" parameterType="Long" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		where r.role_id = #{roleId}
+	</select>
+	
+	<select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		WHERE r.del_flag = '0' and u.user_name = #{userName}
+	</select>
+	
+	<select id="checkRoleNameUnique" parameterType="String" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		 where r.role_name=#{roleName} and r.del_flag = '0' limit 1
+	</select>
+	
+	<select id="checkRoleKeyUnique" parameterType="String" resultMap="SysRoleResult">
+		<include refid="selectRoleVo"/>
+		 where r.role_key=#{roleKey} and r.del_flag = '0' limit 1
+	</select>
+    <select id="page" resultType="com.ruoyi.system.object.vo.SysRolePageVO">
+		select role_id, role_name from sys_role where del_flag=0
+		<if test="name!=null and name!='' ">
+			and `role_name` like concat('%',#{name},'%')
+		</if>
+		order by role_sort asc
+	</select>
+
+
+	<insert id="insertRole" parameterType="SysRole" useGeneratedKeys="true" keyProperty="roleId">
+ 		insert into sys_role(
+ 			<if test="roleId != null and roleId != 0">role_id,</if>
+ 			<if test="roleName != null and roleName != ''">role_name,</if>
+ 			<if test="roleKey != null and roleKey != ''">role_key,</if>
+ 			<if test="roleSort != null">role_sort,</if>
+ 			<if test="dataScope != null and dataScope != ''">data_scope,</if>
+ 			<if test="menuCheckStrictly != null">menu_check_strictly,</if>
+ 			<if test="deptCheckStrictly != null">dept_check_strictly,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="roleId != null and roleId != 0">#{roleId},</if>
+ 			<if test="roleName != null and roleName != ''">#{roleName},</if>
+ 			<if test="roleKey != null and roleKey != ''">#{roleKey},</if>
+ 			<if test="roleSort != null">#{roleSort},</if>
+ 			<if test="dataScope != null and dataScope != ''">#{dataScope},</if>
+ 			<if test="menuCheckStrictly != null">#{menuCheckStrictly},</if>
+ 			<if test="deptCheckStrictly != null">#{deptCheckStrictly},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<update id="updateRole" parameterType="SysRole">
+ 		update sys_role
+ 		<set>
+ 			<if test="roleName != null and roleName != ''">role_name = #{roleName},</if>
+ 			<if test="roleKey != null and roleKey != ''">role_key = #{roleKey},</if>
+ 			<if test="roleSort != null">role_sort = #{roleSort},</if>
+ 			<if test="dataScope != null and dataScope != ''">data_scope = #{dataScope},</if>
+ 			<if test="menuCheckStrictly != null">menu_check_strictly = #{menuCheckStrictly},</if>
+ 			<if test="deptCheckStrictly != null">dept_check_strictly = #{deptCheckStrictly},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where role_id = #{roleId}
+	</update>
+	
+	<delete id="deleteRoleById" parameterType="Long">
+ 		update sys_role set del_flag = '2' where role_id = #{roleId}
+ 	</delete>
+ 	
+ 	<delete id="deleteRoleByIds" parameterType="Long">
+ 	    update sys_role set del_flag = '2' where role_id in
+ 		<foreach collection="array" item="roleId" open="(" separator="," close=")">
+ 			#{roleId}
+        </foreach> 
+ 	</delete>
+ 	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysRoleMenuMapper.xml b/pt-system/target/classes/mapper/system/SysRoleMenuMapper.xml
new file mode 100644
index 0000000..3dc43fc
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysRoleMenuMapper.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysRoleMenuMapper">
+
+	<resultMap type="SysRoleMenu" id="SysRoleMenuResult">
+		<result property="roleId"     column="role_id"      />
+		<result property="menuId"     column="menu_id"      />
+	</resultMap>
+	
+	<select id="checkMenuExistRole" resultType="Integer">
+	    select count(1) from sys_role_menu where menu_id = #{menuId}
+	</select>
+    <select id="selectMenuIdsByRoleIds" resultType="java.lang.Long">
+		select menu_id from sys_role_menu where role_id in
+		<foreach collection="roleList" item="roleId" open="(" separator="," close=")">
+			#{roleId}
+		</foreach>
+	</select>
+
+    <delete id="deleteRoleMenuByRoleId" parameterType="Long">
+		delete from sys_role_menu where role_id=#{roleId}
+	</delete>
+	
+	<delete id="deleteRoleMenu" parameterType="Long">
+ 		delete from sys_role_menu where role_id in
+ 		<foreach collection="array" item="roleId" open="(" separator="," close=")">
+ 			#{roleId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchRoleMenu">
+		insert into sys_role_menu(role_id, menu_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.roleId},#{item.menuId})
+		</foreach>
+	</insert>
+	<insert id="insert">
+		INSERT INTO sys_role_menu (role_id, menu_id)
+		VALUES
+		<foreach collection="menuIds" item="menuId" separator=",">
+			(#{roleId}, #{menuId})
+		</foreach>
+	</insert>
+
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysUserMapper.xml b/pt-system/target/classes/mapper/system/SysUserMapper.xml
new file mode 100644
index 0000000..30f57a4
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysUserMapper.xml
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysUserMapper">
+
+    <resultMap type="SysUser" id="SysUserResult">
+        <id     property="userId"       column="user_id"      />
+        <result property="deptId"       column="dept_id"      />
+        <result property="userName"     column="user_name"    />
+        <result property="nickName"     column="nick_name"    />
+        <result property="email"        column="email"        />
+        <result property="phonenumber"  column="phonenumber"  />
+        <result property="sex"          column="sex"          />
+        <result property="avatar"       column="avatar"       />
+        <result property="password"     column="password"     />
+        <result property="status"       column="status"       />
+        <result property="delFlag"      column="del_flag"     />
+        <result property="loginIp"      column="login_ip"     />
+        <result property="loginDate"    column="login_date"   />
+        <result property="createBy"     column="create_by"    />
+        <result property="createTime"   column="create_time"  />
+        <result property="updateBy"     column="update_by"    />
+        <result property="updateTime"   column="update_time"  />
+        <result property="remark"       column="remark"       />
+        <association property="dept"    javaType="SysDept"         resultMap="deptResult" />
+        <collection  property="roles"   javaType="java.util.List"  resultMap="RoleResult" />
+    </resultMap>
+	
+    <resultMap id="deptResult" type="SysDept">
+        <id     property="deptId"    column="dept_id"     />
+        <result property="parentId"  column="parent_id"   />
+        <result property="deptName"  column="dept_name"   />
+        <result property="ancestors" column="ancestors"   />
+        <result property="orderNum"  column="order_num"   />
+        <result property="leader"    column="leader"      />
+        <result property="status"    column="dept_status" />
+    </resultMap>
+	
+    <resultMap id="RoleResult" type="SysRole">
+        <id     property="roleId"       column="role_id"        />
+        <result property="roleName"     column="role_name"      />
+        <result property="roleKey"      column="role_key"       />
+        <result property="roleSort"     column="role_sort"      />
+        <result property="dataScope"    column="data_scope"     />
+        <result property="status"       column="role_status"    />
+    </resultMap>
+	
+	<sql id="selectUserVo">
+        select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, 
+        d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
+        r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
+        from sys_user u
+		    left join sys_dept d on u.dept_id = d.dept_id
+		    left join sys_user_role ur on u.user_id = ur.user_id
+		    left join sys_role r on r.role_id = ur.role_id
+    </sql>
+    
+    <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
+		select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
+		left join sys_dept d on u.dept_id = d.dept_id
+		where u.del_flag = '0'
+		<if test="userId != null and userId != 0">
+			AND u.user_id = #{userId}
+		</if>
+		<if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="status != null and status != ''">
+			AND u.status = #{status}
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+			AND date_format(u.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
+		</if>
+		<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+			AND date_format(u.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
+		</if>
+		<if test="deptId != null and deptId != 0">
+			AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
+	<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">
+	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
+	    from sys_user u
+			 left join sys_dept d on u.dept_id = d.dept_id
+			 left join sys_user_role ur on u.user_id = ur.user_id
+			 left join sys_role r on r.role_id = ur.role_id
+	    where u.del_flag = '0' and r.role_id = #{roleId}
+	    <if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
+	<select id="selectUnallocatedList" parameterType="SysUser" resultMap="SysUserResult">
+	    select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
+	    from sys_user u
+			 left join sys_dept d on u.dept_id = d.dept_id
+			 left join sys_user_role ur on u.user_id = ur.user_id
+			 left join sys_role r on r.role_id = ur.role_id
+	    where u.del_flag = '0' and (r.role_id != #{roleId} or r.role_id IS NULL)
+	    and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{roleId})
+	    <if test="userName != null and userName != ''">
+			AND u.user_name like concat('%', #{userName}, '%')
+		</if>
+		<if test="phonenumber != null and phonenumber != ''">
+			AND u.phonenumber like concat('%', #{phonenumber}, '%')
+		</if>
+		<!-- 数据范围过滤 -->
+		${params.dataScope}
+	</select>
+	
+	<select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
+	    <include refid="selectUserVo"/>
+		where u.user_name = #{userName} and u.del_flag = '0'
+	</select>
+	
+	<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
+		<include refid="selectUserVo"/>
+		where u.user_id = #{userId}
+	</select>
+	
+	<select id="checkUserNameUnique" parameterType="String" resultMap="SysUserResult">
+		select user_id, user_name from sys_user where user_name = #{userName} and del_flag = '0' limit 1
+	</select>
+	
+	<select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult">
+		select user_id, phonenumber from sys_user where phonenumber = #{phonenumber} and del_flag = '0' limit 1
+	</select>
+	
+	<select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
+		select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
+	</select>
+    <select id="getSysUserPageList" resultType="com.ruoyi.system.object.vo.SysUserPageListVO">
+		select
+		su.user_id,
+		su.nick_name,
+		su.phonenumber,
+		sd.dept_id,
+		sd.dept_name,
+		sr.role_id,
+		sr.role_name,
+		su.status
+		from sys_user su
+
+		left join sys_dept sd on su.dept_id = sd.dept_id
+		left join sys_user_role sur on su.user_id = sur.user_id
+		left join sys_role sr on sur.role_id =sr.role_id
+		where
+		    su.del_flag='0'
+		<if test="nickName!=null and nickName!= '' ">
+			and su.nick_name like concat('%',#{nickName},'%')
+		</if>
+		<if test="phone!=null and phone!= '' ">
+			and su.phonenumber like concat('%',#{phone},'%')
+		</if>
+		<if test="status!=null and status!= '' ">
+			and su.status =#{status}
+		</if>
+	</select>
+	<select id="getSysUserVO" resultType="com.ruoyi.system.object.vo.SysUserVO">
+		select
+			su.user_id,
+			su.nick_name,
+			su.phonenumber,
+			sd.dept_id,
+			sd.dept_name,
+			sr.role_id,
+			sr.role_name,
+			su.status
+		from sys_user su
+				 left join sys_dept sd on su.dept_id = sd.dept_id
+				 left join sys_user_role sur on su.user_id = sur.user_id
+				 left join sys_role sr on sur.role_id =sr.role_id
+		where
+			su.del_flag='0'
+		and su.user_id=#{userId}
+	</select>
+	<select id="selectuserByPhone" resultType="com.ruoyi.common.core.domain.entity.SysUser">
+		select * from sys_user where del_flag='0' and phonenumber =#{phonenumber}
+	</select>
+
+	<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
+ 		insert into sys_user(
+ 			<if test="userId != null and userId != 0">user_id,</if>
+ 			<if test="deptId != null and deptId != 0">dept_id,</if>
+ 			<if test="userName != null and userName != ''">user_name,</if>
+ 			<if test="nickName != null and nickName != ''">nick_name,</if>
+ 			<if test="email != null and email != ''">email,</if>
+ 			<if test="avatar != null and avatar != ''">avatar,</if>
+ 			<if test="phonenumber != null and phonenumber != ''">phonenumber,</if>
+ 			<if test="sex != null and sex != ''">sex,</if>
+ 			<if test="password != null and password != ''">password,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			create_time
+ 		)values(
+ 			<if test="userId != null and userId != ''">#{userId},</if>
+ 			<if test="deptId != null and deptId != ''">#{deptId},</if>
+ 			<if test="userName != null and userName != ''">#{userName},</if>
+ 			<if test="nickName != null and nickName != ''">#{nickName},</if>
+ 			<if test="email != null and email != ''">#{email},</if>
+ 			<if test="avatar != null and avatar != ''">#{avatar},</if>
+ 			<if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if>
+ 			<if test="sex != null and sex != ''">#{sex},</if>
+ 			<if test="password != null and password != ''">#{password},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			sysdate()
+ 		)
+	</insert>
+	
+	<update id="updateUser" parameterType="SysUser">
+ 		update sys_user
+ 		<set>
+ 			<if test="deptId != null and deptId != 0">dept_id = #{deptId},</if>
+ 			<if test="nickName != null and nickName != ''">nick_name = #{nickName},</if>
+ 			<if test="email != null ">email = #{email},</if>
+ 			<if test="phonenumber != null ">phonenumber = #{phonenumber},</if>
+ 			<if test="sex != null and sex != ''">sex = #{sex},</if>
+ 			<if test="avatar != null and avatar != ''">avatar = #{avatar},</if>
+ 			<if test="password != null and password != ''">password = #{password},</if>
+ 			<if test="status != null and status != ''">status = #{status},</if>
+ 			<if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
+ 			<if test="loginDate != null">login_date = #{loginDate},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			<if test="remark != null">remark = #{remark},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where user_id = #{userId}
+	</update>
+	
+	<update id="updateUserStatus" parameterType="SysUser">
+ 		update sys_user set status = #{status} where user_id = #{userId}
+	</update>
+	
+	<update id="updateUserAvatar" parameterType="SysUser">
+ 		update sys_user set avatar = #{avatar} where user_name = #{userName}
+	</update>
+	
+	<update id="resetUserPwd" parameterType="SysUser">
+ 		update sys_user set password = #{password} where user_name = #{userName}
+	</update>
+	
+	<delete id="deleteUserById" parameterType="Long">
+ 		update sys_user set del_flag = '2' where user_id = #{userId}
+ 	</delete>
+ 	
+ 	<delete id="deleteUserByIds" parameterType="Long">
+ 		update sys_user set del_flag = '2' where user_id in
+ 		<foreach collection="array" item="userId" open="(" separator="," close=")">
+ 			#{userId}
+        </foreach> 
+ 	</delete>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysUserPostMapper.xml b/pt-system/target/classes/mapper/system/SysUserPostMapper.xml
new file mode 100644
index 0000000..2b90bc4
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysUserPostMapper.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysUserPostMapper">
+
+	<resultMap type="SysUserPost" id="SysUserPostResult">
+		<result property="userId"     column="user_id"      />
+		<result property="postId"     column="post_id"      />
+	</resultMap>
+
+	<delete id="deleteUserPostByUserId" parameterType="Long">
+		delete from sys_user_post where user_id=#{userId}
+	</delete>
+	
+	<select id="countUserPostById" resultType="Integer">
+	    select count(1) from sys_user_post where post_id=#{postId}  
+	</select>
+	
+	<delete id="deleteUserPost" parameterType="Long">
+ 		delete from sys_user_post where user_id in
+ 		<foreach collection="array" item="userId" open="(" separator="," close=")">
+ 			#{userId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchUserPost">
+		insert into sys_user_post(user_id, post_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.userId},#{item.postId})
+		</foreach>
+	</insert>
+	
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/classes/mapper/system/SysUserRoleMapper.xml b/pt-system/target/classes/mapper/system/SysUserRoleMapper.xml
new file mode 100644
index 0000000..8fadc85
--- /dev/null
+++ b/pt-system/target/classes/mapper/system/SysUserRoleMapper.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.system.mapper.SysUserRoleMapper">
+
+	<resultMap type="SysUserRole" id="SysUserRoleResult">
+		<result property="userId"     column="user_id"      />
+		<result property="roleId"     column="role_id"      />
+	</resultMap>
+
+	<delete id="deleteUserRoleByUserId" parameterType="Long">
+		delete from sys_user_role where user_id=#{userId}
+	</delete>
+	
+	<select id="countUserRoleByRoleId" resultType="Integer">
+	    select count(1) from sys_user_role where role_id=#{roleId}  
+	</select>
+	<select id="selectRoleByUserId" resultType="com.ruoyi.common.core.domain.entity.SysRole">
+		select * from sys_user_role where user_id=#{userId}
+	</select>
+
+	<delete id="deleteUserRole" parameterType="Long">
+ 		delete from sys_user_role where user_id in
+ 		<foreach collection="array" item="userId" open="(" separator="," close=")">
+ 			#{userId}
+        </foreach> 
+ 	</delete>
+	
+	<insert id="batchUserRole">
+		insert into sys_user_role(user_id, role_id) values
+		<foreach item="item" index="index" collection="list" separator=",">
+			(#{item.userId},#{item.roleId})
+		</foreach>
+	</insert>
+    <insert id="insert">
+		insert into sys_user_role(user_id, role_id) value
+			(#{userId},#{roleId})
+	</insert>
+
+    <delete id="deleteUserRoleInfo" parameterType="SysUserRole">
+		delete from sys_user_role where user_id=#{userId} and role_id=#{roleId}
+	</delete>
+	
+	<delete id="deleteUserRoleInfos">
+	    delete from sys_user_role where role_id=#{roleId} and user_id in
+ 	    <foreach collection="userIds" item="userId" open="(" separator="," close=")">
+ 	        #{userId}
+            </foreach> 
+	</delete>
+</mapper> 
\ No newline at end of file
diff --git a/pt-system/target/maven-archiver/pom.properties b/pt-system/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..aeb2cca
--- /dev/null
+++ b/pt-system/target/maven-archiver/pom.properties
@@ -0,0 +1,3 @@
+artifactId=pt-system
+groupId=com.pt
+version=3.8.9
diff --git a/pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..dcb0618
--- /dev/null
+++ b/pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,53 @@
+com\ruoyi\system\mapper\SysOperLogMapper.class
+com\ruoyi\system\domain\SysUserRole.class
+com\ruoyi\system\mapper\SysRoleMapper.class
+com\ruoyi\system\service\ISysDeptService.class
+com\ruoyi\system\service\ISysDictTypeService.class
+com\ruoyi\system\domain\SysOperLog.class
+com\ruoyi\system\domain\SysRoleDept.class
+com\ruoyi\system\mapper\SysConfigMapper.class
+com\ruoyi\system\domain\SysUserPost.class
+com\ruoyi\system\service\impl\SysDictDataServiceImpl.class
+com\ruoyi\system\domain\SysRoleMenu.class
+com\ruoyi\system\service\ISysConfigService.class
+com\ruoyi\system\service\ISysUserService.class
+com\ruoyi\system\domain\vo\MetaVo.class
+com\ruoyi\system\mapper\SysNoticeMapper.class
+com\ruoyi\system\domain\SysConfig.class
+com\ruoyi\system\service\ISysUserOnlineService.class
+com\ruoyi\system\service\ISysPostService.class
+com\ruoyi\system\domain\SysCache.class
+com\ruoyi\system\mapper\SysPostMapper.class
+com\ruoyi\system\service\impl\SysMenuServiceImpl.class
+com\ruoyi\system\service\ISysMenuService.class
+com\ruoyi\system\domain\SysPost.class
+com\ruoyi\system\service\impl\SysUserServiceImpl.class
+com\ruoyi\system\mapper\SysDictTypeMapper.class
+com\ruoyi\system\service\ISysLogininforService.class
+com\ruoyi\system\domain\SysLogininfor.class
+com\ruoyi\system\service\impl\SysConfigServiceImpl.class
+com\ruoyi\system\service\impl\SysLogininforServiceImpl.class
+com\ruoyi\system\mapper\SysDeptMapper.class
+com\ruoyi\system\mapper\SysUserPostMapper.class
+com\ruoyi\system\object\response\index\IndexMessageResponse.class
+com\ruoyi\system\domain\SysUserOnline.class
+com\ruoyi\system\mapper\SysUserRoleMapper.class
+com\ruoyi\system\service\impl\SysRoleServiceImpl.class
+com\ruoyi\system\service\impl\SysDictTypeServiceImpl.class
+com\ruoyi\system\domain\vo\RouterVo.class
+com\ruoyi\system\service\ISysNoticeService.class
+com\ruoyi\system\mapper\SysUserMapper.class
+com\ruoyi\system\mapper\SysLogininforMapper.class
+com\ruoyi\system\service\impl\SysOperLogServiceImpl.class
+com\ruoyi\system\service\ISysDictDataService.class
+com\ruoyi\system\mapper\SysMenuMapper.class
+com\ruoyi\system\service\ISysRoleService.class
+com\ruoyi\system\service\impl\SysNoticeServiceImpl.class
+com\ruoyi\system\mapper\SysDictDataMapper.class
+com\ruoyi\system\service\ISysOperLogService.class
+com\ruoyi\system\service\impl\SysUserOnlineServiceImpl.class
+com\ruoyi\system\service\impl\SysDeptServiceImpl.class
+com\ruoyi\system\service\impl\SysPostServiceImpl.class
+com\ruoyi\system\mapper\SysRoleMenuMapper.class
+com\ruoyi\system\domain\SysNotice.class
+com\ruoyi\system\mapper\SysRoleDeptMapper.class
diff --git a/pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..97fd89f
--- /dev/null
+++ b/pt-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,53 @@
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysMenuServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysRoleMenu.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysOperLog.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysRoleDeptMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysMenuService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysLogininforServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysUserOnlineServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysDictDataService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysPostServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysDictDataMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysUserRoleMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysUserPost.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysConfigServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysRoleServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysNoticeService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysDeptServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysConfigMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysDictTypeServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysMenuMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysUserMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysRoleService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\vo\RouterVo.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysDeptMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysUserService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysLogininforService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysRoleMenuMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysNotice.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysUserRole.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\vo\MetaVo.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysOperLogService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\object\response\index\IndexMessageResponse.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysPost.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysDictTypeService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysCache.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysUserPostMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysDictTypeMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysDictDataServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysDeptService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysRoleMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysOperLogServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysUserServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysConfigService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysConfig.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\impl\SysNoticeServiceImpl.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysPostMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysNoticeMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysPostService.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysLogininforMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\mapper\SysOperLogMapper.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysLogininfor.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysRoleDept.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\domain\SysUserOnline.java
+E:\hlg资料\项目\paotui\pt-system\src\main\java\com\ruoyi\system\service\ISysUserOnlineService.java

--
Gitblit v1.7.1