Repository: fl1906/music-city Branch: master Commit: 5df22da88efb Files: 1013 Total size: 14.9 MB Directory structure: gitextract_aag72qvb/ ├── .editorconfig ├── .gitee/ │ ├── ISSUE_TEMPLATE.zh-CN.md │ └── PULL_REQUEST_TEMPLATE.zh-CN.md ├── .gitignore ├── .run/ │ ├── ruoyi-monitor-admin.run.xml │ ├── ruoyi-server.run.xml │ └── ruoyi-xxl-job-admin.run.xml ├── .vscode/ │ └── settings.json ├── LICENSE ├── PaiZhiCheng/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ ├── Main.java │ │ └── top/ │ │ └── flya/ │ │ └── system/ │ │ ├── common/ │ │ │ ├── BatchUtils.java │ │ │ └── CheckUtils.java │ │ ├── config/ │ │ │ ├── ClientCache.java │ │ │ ├── DbTemplate.java │ │ │ ├── Event.java │ │ │ ├── ServerConfig.java │ │ │ ├── ServerRunner.java │ │ │ └── WsConfig.java │ │ ├── controller/ │ │ │ ├── GaoDeMapController.java │ │ │ ├── PzcActivityConnArtistController.java │ │ │ ├── PzcActivityConnIntroController.java │ │ │ ├── PzcActivityConnTagController.java │ │ │ ├── PzcActivityController.java │ │ │ ├── PzcActivityGroupApplyController.java │ │ │ ├── PzcActivityGroupController.java │ │ │ ├── PzcArtistController.java │ │ │ ├── PzcIntroController.java │ │ │ ├── PzcOfficialController.java │ │ │ ├── PzcOrderController.java │ │ │ ├── PzcOrganizerController.java │ │ │ ├── PzcOrganizerTicketController.java │ │ │ ├── PzcRegionController.java │ │ │ ├── PzcTagController.java │ │ │ ├── PzcUserCollectController.java │ │ │ ├── PzcUserController.java │ │ │ ├── PzcUserHistoryController.java │ │ │ ├── PzcUserPhotoController.java │ │ │ ├── PzcUserTalkController.java │ │ │ ├── PzcViewPagerController.java │ │ │ └── WxUserController.java │ │ ├── domain/ │ │ │ ├── PzcActivity.java │ │ │ ├── PzcActivityConnArtist.java │ │ │ ├── PzcActivityConnIntro.java │ │ │ ├── PzcActivityConnTag.java │ │ │ ├── PzcActivityGroup.java │ │ │ ├── PzcActivityGroupApply.java │ │ │ ├── PzcArtist.java │ │ │ ├── PzcIntro.java │ │ │ ├── PzcOfficial.java │ │ │ ├── PzcOrder.java │ │ │ ├── PzcOrganizer.java │ │ │ ├── PzcOrganizerTicket.java │ │ │ ├── PzcRegion.java │ │ │ ├── PzcTag.java │ │ │ ├── PzcUser.java │ │ │ ├── PzcUserCollect.java │ │ │ ├── PzcUserHistory.java │ │ │ ├── PzcUserPhoto.java │ │ │ ├── PzcUserTalk.java │ │ │ ├── PzcViewPager.java │ │ │ ├── bo/ │ │ │ │ ├── PayOrderBo.java │ │ │ │ ├── PzcActivityBo.java │ │ │ │ ├── PzcActivityConnArtistBo.java │ │ │ │ ├── PzcActivityConnIntroBo.java │ │ │ │ ├── PzcActivityConnTagBo.java │ │ │ │ ├── PzcActivityGroupApplyBo.java │ │ │ │ ├── PzcActivityGroupBo.java │ │ │ │ ├── PzcArtistBo.java │ │ │ │ ├── PzcIntroBo.java │ │ │ │ ├── PzcOfficialBo.java │ │ │ │ ├── PzcOrderBo.java │ │ │ │ ├── PzcOrganizerBo.java │ │ │ │ ├── PzcOrganizerTicketBo.java │ │ │ │ ├── PzcRegionBo.java │ │ │ │ ├── PzcTagBo.java │ │ │ │ ├── PzcUserBo.java │ │ │ │ ├── PzcUserCollectBo.java │ │ │ │ ├── PzcUserHistoryBo.java │ │ │ │ ├── PzcUserPhotoBo.java │ │ │ │ ├── PzcUserTalkBo.java │ │ │ │ ├── PzcViewPagerBo.java │ │ │ │ ├── RefurbishBo.java │ │ │ │ ├── Resource.java │ │ │ │ ├── SuccessCallBackObjBo.java │ │ │ │ ├── UpdateMoneyBo.java │ │ │ │ └── WxzApplyBo.java │ │ │ └── vo/ │ │ │ ├── PzcActivityConnArtistVo.java │ │ │ ├── PzcActivityConnIntroVo.java │ │ │ ├── PzcActivityConnTagVo.java │ │ │ ├── PzcActivityGroupApplyVo.java │ │ │ ├── PzcActivityGroupVo.java │ │ │ ├── PzcActivityVo.java │ │ │ ├── PzcArtistVo.java │ │ │ ├── PzcIntroVo.java │ │ │ ├── PzcOfficialVo.java │ │ │ ├── PzcOrderVo.java │ │ │ ├── PzcOrganizerTicketVo.java │ │ │ ├── PzcOrganizerVo.java │ │ │ ├── PzcRegionVo.java │ │ │ ├── PzcTagVo.java │ │ │ ├── PzcUserCollectVo.java │ │ │ ├── PzcUserHistoryVo.java │ │ │ ├── PzcUserPhotoVo.java │ │ │ ├── PzcUserTalkVo.java │ │ │ ├── PzcUserVo.java │ │ │ ├── PzcViewPagerVo.java │ │ │ └── RefurbishVO.java │ │ ├── entity/ │ │ │ ├── Activity.java │ │ │ ├── ActivityConnArtist.java │ │ │ ├── ActivityConnIntro.java │ │ │ ├── ActivityConnTag.java │ │ │ ├── Artist.java │ │ │ ├── Event.java │ │ │ ├── FLBaseEntity.java │ │ │ ├── Intro.java │ │ │ ├── Organizer.java │ │ │ ├── OrganizerTicket.java │ │ │ ├── Price.java │ │ │ ├── Region.java │ │ │ ├── Tag.java │ │ │ └── ViewPager.java │ │ ├── handel/ │ │ │ ├── MessageEventHandler.java │ │ │ └── WxPayInitHandel.java │ │ ├── mapper/ │ │ │ ├── PzcActivityConnArtistMapper.java │ │ │ ├── PzcActivityConnIntroMapper.java │ │ │ ├── PzcActivityConnTagMapper.java │ │ │ ├── PzcActivityGroupApplyMapper.java │ │ │ ├── PzcActivityGroupMapper.java │ │ │ ├── PzcActivityMapper.java │ │ │ ├── PzcArtistMapper.java │ │ │ ├── PzcIntroMapper.java │ │ │ ├── PzcOfficialMapper.java │ │ │ ├── PzcOrderMapper.java │ │ │ ├── PzcOrganizerMapper.java │ │ │ ├── PzcOrganizerTicketMapper.java │ │ │ ├── PzcRegionMapper.java │ │ │ ├── PzcTagMapper.java │ │ │ ├── PzcUserCollectMapper.java │ │ │ ├── PzcUserHistoryMapper.java │ │ │ ├── PzcUserMapper.java │ │ │ ├── PzcUserPhotoMapper.java │ │ │ ├── PzcUserTalkMapper.java │ │ │ ├── PzcViewPagerMapper.java │ │ │ └── RegionTreeMapper.java │ │ ├── service/ │ │ │ ├── IPzcActivityConnArtistService.java │ │ │ ├── IPzcActivityConnIntroService.java │ │ │ ├── IPzcActivityConnTagService.java │ │ │ ├── IPzcActivityGroupApplyService.java │ │ │ ├── IPzcActivityGroupService.java │ │ │ ├── IPzcActivityService.java │ │ │ ├── IPzcArtistService.java │ │ │ ├── IPzcIntroService.java │ │ │ ├── IPzcOfficialService.java │ │ │ ├── IPzcOrderService.java │ │ │ ├── IPzcOrganizerService.java │ │ │ ├── IPzcOrganizerTicketService.java │ │ │ ├── IPzcRegionService.java │ │ │ ├── IPzcTagService.java │ │ │ ├── IPzcUserCollectService.java │ │ │ ├── IPzcUserHistoryService.java │ │ │ ├── IPzcUserPhotoService.java │ │ │ ├── IPzcUserService.java │ │ │ ├── IPzcUserTalkService.java │ │ │ ├── IPzcViewPagerService.java │ │ │ └── impl/ │ │ │ ├── PzcActivityConnArtistServiceImpl.java │ │ │ ├── PzcActivityConnIntroServiceImpl.java │ │ │ ├── PzcActivityConnTagServiceImpl.java │ │ │ ├── PzcActivityGroupApplyServiceImpl.java │ │ │ ├── PzcActivityGroupServiceImpl.java │ │ │ ├── PzcActivityServiceImpl.java │ │ │ ├── PzcArtistServiceImpl.java │ │ │ ├── PzcIntroServiceImpl.java │ │ │ ├── PzcOfficialServiceImpl.java │ │ │ ├── PzcOrderServiceImpl.java │ │ │ ├── PzcOrganizerServiceImpl.java │ │ │ ├── PzcOrganizerTicketServiceImpl.java │ │ │ ├── PzcRegionServiceImpl.java │ │ │ ├── PzcTagServiceImpl.java │ │ │ ├── PzcUserCollectServiceImpl.java │ │ │ ├── PzcUserHistoryServiceImpl.java │ │ │ ├── PzcUserPhotoServiceImpl.java │ │ │ ├── PzcUserServiceImpl.java │ │ │ ├── PzcUserTalkServiceImpl.java │ │ │ └── PzcViewPagerServiceImpl.java │ │ ├── utils/ │ │ │ ├── ActivityUtils.java │ │ │ ├── CreateSign.java │ │ │ ├── MyPrivateKey.java │ │ │ ├── WxUtils.java │ │ │ ├── gaode/ │ │ │ │ ├── GaoDeEnum.java │ │ │ │ └── GaoDeMapUtil.java │ │ │ ├── map/ │ │ │ │ ├── City.java │ │ │ │ ├── CitySql.java │ │ │ │ ├── Maps.java │ │ │ │ └── city.json │ │ │ └── sensitivewordsfiliter/ │ │ │ ├── BaseSearch.java │ │ │ ├── TrieNode.java │ │ │ ├── TrieNode2.java │ │ │ ├── WordsSearch.java │ │ │ ├── WordsSearchResult.java │ │ │ └── WorldsFilterUtils.java │ │ └── xxlJob/ │ │ ├── ScheduledExecutorUtils.java │ │ ├── WxHandler.java │ │ └── diy/ │ │ ├── JobLoginService.java │ │ ├── TestController.java │ │ ├── XxlJobGroup.java │ │ └── XxlJobInfo.java │ └── resources/ │ ├── apiclient_key.pem │ ├── mapper/ │ │ └── system/ │ │ ├── PzcActivityConnArtistMapper.xml │ │ ├── PzcActivityConnIntroMapper.xml │ │ ├── PzcActivityConnTagMapper.xml │ │ ├── PzcActivityGroupApplyMapper.xml │ │ ├── PzcActivityGroupMapper.xml │ │ ├── PzcActivityMapper.xml │ │ ├── PzcArtistMapper.xml │ │ ├── PzcIntroMapper.xml │ │ ├── PzcOfficialMapper.xml │ │ ├── PzcOrderMapper.xml │ │ ├── PzcOrganizerMapper.xml │ │ ├── PzcOrganizerTicketMapper.xml │ │ ├── PzcRegionMapper.xml │ │ ├── PzcTagMapper.xml │ │ ├── PzcUserCollectMapper.xml │ │ ├── PzcUserHistoryMapper.xml │ │ ├── PzcUserMapper.xml │ │ ├── PzcUserPhotoMapper.xml │ │ ├── PzcUserTalkMapper.xml │ │ └── PzcViewPagerMapper.xml │ ├── static/ │ │ ├── bootstrap.css │ │ ├── index.html │ │ ├── index2.html │ │ └── js/ │ │ └── socket.io/ │ │ └── socket.io.js │ └── wordsfilter/ │ ├── sensi_words.txt │ └── sensi_words2.txt ├── README.md ├── pom.xml ├── ruoyi-admin/ │ ├── Dockerfile │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── top/ │ │ └── flya/ │ │ ├── RuoYiApplication.java │ │ ├── RuoYiServletInitializer.java │ │ └── web/ │ │ └── controller/ │ │ ├── common/ │ │ │ └── CaptchaController.java │ │ ├── monitor/ │ │ │ ├── CacheController.java │ │ │ ├── SysLogininforController.java │ │ │ ├── SysOperlogController.java │ │ │ └── SysUserOnlineController.java │ │ └── system/ │ │ ├── SysConfigController.java │ │ ├── SysDeptController.java │ │ ├── SysDictDataController.java │ │ ├── SysDictTypeController.java │ │ ├── SysIndexController.java │ │ ├── SysLoginController.java │ │ ├── SysMenuController.java │ │ ├── SysNoticeController.java │ │ ├── SysOssConfigController.java │ │ ├── SysOssController.java │ │ ├── SysPostController.java │ │ ├── SysProfileController.java │ │ ├── SysRegisterController.java │ │ ├── SysRoleController.java │ │ └── SysUserController.java │ └── resources/ │ ├── Dockerfile │ ├── apiclient_key.pem │ ├── application-dev.yml │ ├── application.yml │ ├── banner.txt │ ├── i18n/ │ │ ├── messages.properties │ │ ├── messages_en_US.properties │ │ └── messages_zh_CN.properties │ ├── ip2region.xdb │ ├── logback-plus.xml │ └── spy.properties ├── ruoyi-common/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── top/ │ └── flya/ │ └── common/ │ ├── annotation/ │ │ ├── CellMerge.java │ │ ├── DataColumn.java │ │ ├── DataPermission.java │ │ ├── DictDataMapper.java │ │ ├── EncryptField.java │ │ ├── ExcelDictFormat.java │ │ ├── ExcelEnumFormat.java │ │ ├── Log.java │ │ ├── RateLimiter.java │ │ ├── RepeatSubmit.java │ │ ├── Sensitive.java │ │ ├── Translation.java │ │ └── TranslationType.java │ ├── captcha/ │ │ └── UnsignedMathGenerator.java │ ├── config/ │ │ └── RuoYiConfig.java │ ├── constant/ │ │ ├── CacheConstants.java │ │ ├── CacheNames.java │ │ ├── Constants.java │ │ ├── GenConstants.java │ │ ├── HttpStatus.java │ │ ├── TransConstant.java │ │ └── UserConstants.java │ ├── convert/ │ │ ├── ExcelBigNumberConvert.java │ │ ├── ExcelDictConvert.java │ │ └── ExcelEnumConvert.java │ ├── core/ │ │ ├── controller/ │ │ │ └── BaseController.java │ │ ├── domain/ │ │ │ ├── BaseEntity.java │ │ │ ├── PageQuery.java │ │ │ ├── R.java │ │ │ ├── TreeEntity.java │ │ │ ├── dto/ │ │ │ │ ├── RoleDTO.java │ │ │ │ └── UserOnlineDTO.java │ │ │ ├── entity/ │ │ │ │ ├── SysDept.java │ │ │ │ ├── SysDictData.java │ │ │ │ ├── SysDictType.java │ │ │ │ ├── SysMenu.java │ │ │ │ ├── SysRole.java │ │ │ │ └── SysUser.java │ │ │ ├── event/ │ │ │ │ ├── LogininforEvent.java │ │ │ │ └── OperLogEvent.java │ │ │ └── model/ │ │ │ ├── EmailLoginBody.java │ │ │ ├── LoginBody.java │ │ │ ├── LoginUser.java │ │ │ ├── RegisterBody.java │ │ │ ├── SmsLoginBody.java │ │ │ └── XcxLoginUser.java │ │ ├── mapper/ │ │ │ └── BaseMapperPlus.java │ │ ├── page/ │ │ │ └── TableDataInfo.java │ │ ├── service/ │ │ │ ├── ConfigService.java │ │ │ ├── DeptService.java │ │ │ ├── DictService.java │ │ │ ├── OssService.java │ │ │ ├── SensitiveService.java │ │ │ └── UserService.java │ │ └── validate/ │ │ ├── AddGroup.java │ │ ├── EditGroup.java │ │ └── QueryGroup.java │ ├── encrypt/ │ │ ├── EncryptContext.java │ │ ├── IEncryptor.java │ │ └── encryptor/ │ │ ├── AbstractEncryptor.java │ │ ├── AesEncryptor.java │ │ ├── Base64Encryptor.java │ │ ├── RsaEncryptor.java │ │ ├── Sm2Encryptor.java │ │ └── Sm4Encryptor.java │ ├── enums/ │ │ ├── AlgorithmType.java │ │ ├── BusinessStatus.java │ │ ├── BusinessType.java │ │ ├── CaptchaCategory.java │ │ ├── CaptchaType.java │ │ ├── DataBaseType.java │ │ ├── DataScopeType.java │ │ ├── DeviceType.java │ │ ├── EncodeType.java │ │ ├── HttpMethod.java │ │ ├── LimitType.java │ │ ├── LoginType.java │ │ ├── OperatorType.java │ │ ├── SensitiveStrategy.java │ │ ├── UserStatus.java │ │ └── UserType.java │ ├── excel/ │ │ ├── CellMergeStrategy.java │ │ ├── DefaultExcelListener.java │ │ ├── DefaultExcelResult.java │ │ ├── ExcelListener.java │ │ └── ExcelResult.java │ ├── exception/ │ │ ├── DemoModeException.java │ │ ├── GlobalException.java │ │ ├── ServiceException.java │ │ ├── UtilException.java │ │ ├── base/ │ │ │ └── BaseException.java │ │ ├── file/ │ │ │ ├── FileException.java │ │ │ ├── FileNameLengthLimitExceededException.java │ │ │ └── FileSizeLimitExceededException.java │ │ └── user/ │ │ ├── CaptchaException.java │ │ ├── CaptchaExpireException.java │ │ ├── UserException.java │ │ ├── UserPasswordNotMatchException.java │ │ └── UserPasswordRetryLimitExceedException.java │ ├── filter/ │ │ ├── RepeatableFilter.java │ │ ├── RepeatedlyRequestWrapper.java │ │ ├── XssFilter.java │ │ └── XssHttpServletRequestWrapper.java │ ├── helper/ │ │ ├── DataBaseHelper.java │ │ ├── DataPermissionHelper.java │ │ └── LoginHelper.java │ ├── jackson/ │ │ ├── DictDataJsonSerializer.java │ │ └── SensitiveJsonSerializer.java │ ├── translation/ │ │ ├── TranslationInterface.java │ │ ├── handler/ │ │ │ ├── TranslationBeanSerializerModifier.java │ │ │ └── TranslationHandler.java │ │ └── impl/ │ │ ├── DeptNameTranslationImpl.java │ │ ├── DictTypeTranslationImpl.java │ │ ├── OssUrlTranslationImpl.java │ │ └── UserNameTranslationImpl.java │ ├── utils/ │ │ ├── BeanCopyUtils.java │ │ ├── DateUtils.java │ │ ├── EncryptUtils.java │ │ ├── JsonUtils.java │ │ ├── MessageUtils.java │ │ ├── ServletUtils.java │ │ ├── StreamUtils.java │ │ ├── StringUtils.java │ │ ├── Threads.java │ │ ├── TreeBuildUtils.java │ │ ├── ValidatorUtils.java │ │ ├── email/ │ │ │ └── MailUtils.java │ │ ├── file/ │ │ │ ├── FileUtils.java │ │ │ └── MimeTypeUtils.java │ │ ├── ip/ │ │ │ ├── AddressUtils.java │ │ │ └── RegionUtils.java │ │ ├── poi/ │ │ │ └── ExcelUtil.java │ │ ├── redis/ │ │ │ ├── CacheUtils.java │ │ │ ├── QueueUtils.java │ │ │ └── RedisUtils.java │ │ ├── reflect/ │ │ │ └── ReflectUtils.java │ │ ├── spring/ │ │ │ └── SpringUtils.java │ │ └── sql/ │ │ └── SqlUtil.java │ └── xss/ │ ├── Xss.java │ └── XssValidator.java ├── ruoyi-extend/ │ ├── pom.xml │ ├── ruoyi-monitor-admin/ │ │ ├── Dockerfile │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── top/ │ │ │ └── flya/ │ │ │ └── monitor/ │ │ │ └── admin/ │ │ │ ├── MonitorAdminApplication.java │ │ │ ├── config/ │ │ │ │ ├── AdminServerConfig.java │ │ │ │ └── SecurityConfig.java │ │ │ └── notifier/ │ │ │ └── CustomNotifier.java │ │ └── resources/ │ │ ├── application.yml │ │ ├── banner.txt │ │ └── logback-plus.xml │ └── ruoyi-xxl-job-admin/ │ ├── Dockerfile │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── xxl/ │ │ └── job/ │ │ └── admin/ │ │ ├── XxlJobAdminApplication.java │ │ ├── controller/ │ │ │ ├── IndexController.java │ │ │ ├── JobApiController.java │ │ │ ├── JobCodeController.java │ │ │ ├── JobGroupController.java │ │ │ ├── JobInfoController.java │ │ │ ├── JobLogController.java │ │ │ ├── UserController.java │ │ │ ├── annotation/ │ │ │ │ └── PermissionLimit.java │ │ │ ├── interceptor/ │ │ │ │ ├── CookieInterceptor.java │ │ │ │ ├── PermissionInterceptor.java │ │ │ │ └── WebMvcConfig.java │ │ │ └── resolver/ │ │ │ └── WebExceptionResolver.java │ │ ├── core/ │ │ │ ├── alarm/ │ │ │ │ ├── JobAlarm.java │ │ │ │ ├── JobAlarmer.java │ │ │ │ └── impl/ │ │ │ │ └── EmailJobAlarm.java │ │ │ ├── complete/ │ │ │ │ └── XxlJobCompleter.java │ │ │ ├── conf/ │ │ │ │ └── XxlJobAdminConfig.java │ │ │ ├── cron/ │ │ │ │ └── CronExpression.java │ │ │ ├── exception/ │ │ │ │ └── XxlJobException.java │ │ │ ├── model/ │ │ │ │ ├── XxlJobGroup.java │ │ │ │ ├── XxlJobInfo.java │ │ │ │ ├── XxlJobLog.java │ │ │ │ ├── XxlJobLogGlue.java │ │ │ │ ├── XxlJobLogReport.java │ │ │ │ ├── XxlJobRegistry.java │ │ │ │ └── XxlJobUser.java │ │ │ ├── old/ │ │ │ │ ├── RemoteHttpJobBean.java │ │ │ │ ├── XxlJobDynamicScheduler.java │ │ │ │ └── XxlJobThreadPool.java │ │ │ ├── route/ │ │ │ │ ├── ExecutorRouteStrategyEnum.java │ │ │ │ ├── ExecutorRouter.java │ │ │ │ └── strategy/ │ │ │ │ ├── ExecutorRouteBusyover.java │ │ │ │ ├── ExecutorRouteConsistentHash.java │ │ │ │ ├── ExecutorRouteFailover.java │ │ │ │ ├── ExecutorRouteFirst.java │ │ │ │ ├── ExecutorRouteLFU.java │ │ │ │ ├── ExecutorRouteLRU.java │ │ │ │ ├── ExecutorRouteLast.java │ │ │ │ ├── ExecutorRouteRandom.java │ │ │ │ └── ExecutorRouteRound.java │ │ │ ├── scheduler/ │ │ │ │ ├── MisfireStrategyEnum.java │ │ │ │ ├── ScheduleTypeEnum.java │ │ │ │ └── XxlJobScheduler.java │ │ │ ├── thread/ │ │ │ │ ├── JobCompleteHelper.java │ │ │ │ ├── JobFailMonitorHelper.java │ │ │ │ ├── JobLogReportHelper.java │ │ │ │ ├── JobRegistryHelper.java │ │ │ │ ├── JobScheduleHelper.java │ │ │ │ └── JobTriggerPoolHelper.java │ │ │ ├── trigger/ │ │ │ │ ├── TriggerTypeEnum.java │ │ │ │ └── XxlJobTrigger.java │ │ │ └── util/ │ │ │ ├── CookieUtil.java │ │ │ ├── FtlUtil.java │ │ │ ├── I18nUtil.java │ │ │ ├── JacksonUtil.java │ │ │ └── LocalCacheUtil.java │ │ ├── dao/ │ │ │ ├── XxlJobGroupDao.java │ │ │ ├── XxlJobInfoDao.java │ │ │ ├── XxlJobLogDao.java │ │ │ ├── XxlJobLogGlueDao.java │ │ │ ├── XxlJobLogReportDao.java │ │ │ ├── XxlJobRegistryDao.java │ │ │ └── XxlJobUserDao.java │ │ └── service/ │ │ ├── LoginService.java │ │ ├── XxlJobService.java │ │ └── impl/ │ │ ├── AdminBizImpl.java │ │ └── XxlJobServiceImpl.java │ └── resources/ │ ├── application-dev.yml │ ├── application-prod.yml │ ├── application.yml │ ├── banner.txt │ ├── i18n/ │ │ ├── message_en.properties │ │ ├── message_zh_CN.properties │ │ └── message_zh_TC.properties │ ├── logback-plus.xml │ ├── mybatis-mapper/ │ │ ├── XxlJobGroupMapper.xml │ │ ├── XxlJobInfoMapper.xml │ │ ├── XxlJobLogGlueMapper.xml │ │ ├── XxlJobLogMapper.xml │ │ ├── XxlJobLogReportMapper.xml │ │ ├── XxlJobRegistryMapper.xml │ │ └── XxlJobUserMapper.xml │ ├── static/ │ │ ├── adminlte/ │ │ │ ├── bower_components/ │ │ │ │ ├── PACE/ │ │ │ │ │ └── themes/ │ │ │ │ │ └── blue/ │ │ │ │ │ └── pace-theme-flash.css │ │ │ │ ├── bootstrap-daterangepicker/ │ │ │ │ │ ├── daterangepicker.css │ │ │ │ │ └── daterangepicker.js │ │ │ │ ├── fastclick/ │ │ │ │ │ └── fastclick.js │ │ │ │ └── font-awesome/ │ │ │ │ └── fonts/ │ │ │ │ └── FontAwesome.otf │ │ │ └── plugins/ │ │ │ └── iCheck/ │ │ │ └── square/ │ │ │ └── blue.css │ │ ├── js/ │ │ │ ├── common.1.js │ │ │ ├── index.js │ │ │ ├── jobcode.index.1.js │ │ │ ├── jobgroup.index.1.js │ │ │ ├── jobinfo.index.1.js │ │ │ ├── joblog.detail.1.js │ │ │ ├── joblog.index.1.js │ │ │ ├── login.1.js │ │ │ └── user.index.1.js │ │ └── plugins/ │ │ ├── codemirror/ │ │ │ ├── addon/ │ │ │ │ └── hint/ │ │ │ │ ├── anyword-hint.js │ │ │ │ ├── show-hint.css │ │ │ │ └── show-hint.js │ │ │ ├── lib/ │ │ │ │ ├── codemirror.css │ │ │ │ └── codemirror.js │ │ │ └── mode/ │ │ │ ├── clike/ │ │ │ │ └── clike.js │ │ │ ├── javascript/ │ │ │ │ └── javascript.js │ │ │ ├── php/ │ │ │ │ └── php.js │ │ │ ├── powershell/ │ │ │ │ └── powershell.js │ │ │ ├── python/ │ │ │ │ └── python.js │ │ │ └── shell/ │ │ │ └── shell.js │ │ ├── cronGen/ │ │ │ ├── cronGen.js │ │ │ └── cronGen_en.js │ │ ├── jquery/ │ │ │ └── jquery.cookie.js │ │ └── layer/ │ │ ├── layer.js │ │ └── theme/ │ │ └── default/ │ │ └── layer.css │ └── templates/ │ ├── common/ │ │ ├── common.exception.ftl │ │ └── common.macro.ftl │ ├── help.ftl │ ├── index.ftl │ ├── jobcode/ │ │ └── jobcode.index.ftl │ ├── jobgroup/ │ │ └── jobgroup.index.ftl │ ├── jobinfo/ │ │ └── jobinfo.index.ftl │ ├── joblog/ │ │ ├── joblog.detail.ftl │ │ └── joblog.index.ftl │ ├── login.ftl │ └── user/ │ └── user.index.ftl ├── ruoyi-framework/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── top/ │ └── flya/ │ └── framework/ │ ├── aspectj/ │ │ ├── LogAspect.java │ │ ├── RateLimiterAspect.java │ │ └── RepeatSubmitAspect.java │ ├── config/ │ │ ├── ApplicationConfig.java │ │ ├── AsyncConfig.java │ │ ├── CaptchaConfig.java │ │ ├── EncryptorConfig.java │ │ ├── FilterConfig.java │ │ ├── I18nConfig.java │ │ ├── JacksonConfig.java │ │ ├── MailConfig.java │ │ ├── MybatisPlusConfig.java │ │ ├── RedisConfig.java │ │ ├── ResourcesConfig.java │ │ ├── SaTokenConfig.java │ │ ├── SwaggerConfig.java │ │ ├── ThreadPoolConfig.java │ │ ├── TranslationConfig.java │ │ ├── UndertowConfig.java │ │ ├── ValidatorConfig.java │ │ └── properties/ │ │ ├── CaptchaProperties.java │ │ ├── EncryptorProperties.java │ │ ├── MailProperties.java │ │ ├── RedissonProperties.java │ │ ├── SecurityProperties.java │ │ ├── SwaggerProperties.java │ │ ├── ThreadPoolProperties.java │ │ └── XssProperties.java │ ├── encrypt/ │ │ ├── MybatisDecryptInterceptor.java │ │ └── MybatisEncryptInterceptor.java │ ├── handler/ │ │ ├── AllUrlHandler.java │ │ ├── CreateAndUpdateMetaObjectHandler.java │ │ ├── KeyPrefixHandler.java │ │ ├── OpenApiHandler.java │ │ └── PlusDataPermissionHandler.java │ ├── interceptor/ │ │ ├── PlusDataPermissionInterceptor.java │ │ └── PlusWebInvokeTimeInterceptor.java │ ├── jackson/ │ │ └── BigNumberSerializer.java │ ├── listener/ │ │ └── UserActionListener.java │ ├── manager/ │ │ ├── EncryptorManager.java │ │ ├── PlusSpringCacheManager.java │ │ └── ShutdownManager.java │ ├── satoken/ │ │ ├── dao/ │ │ │ └── PlusSaTokenDao.java │ │ └── service/ │ │ └── SaPermissionImpl.java │ └── web/ │ └── exception/ │ └── GlobalExceptionHandler.java ├── ruoyi-generator/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── top/ │ │ └── flya/ │ │ └── generator/ │ │ ├── config/ │ │ │ └── GenConfig.java │ │ ├── controller/ │ │ │ └── GenController.java │ │ ├── domain/ │ │ │ ├── GenTable.java │ │ │ └── GenTableColumn.java │ │ ├── mapper/ │ │ │ ├── GenTableColumnMapper.java │ │ │ └── GenTableMapper.java │ │ ├── service/ │ │ │ ├── GenTableServiceImpl.java │ │ │ └── IGenTableService.java │ │ └── util/ │ │ ├── GenUtils.java │ │ ├── VelocityInitializer.java │ │ └── VelocityUtils.java │ └── resources/ │ ├── generator.yml │ ├── mapper/ │ │ ├── generator/ │ │ │ ├── GenTableColumnMapper.xml │ │ │ └── GenTableMapper.xml │ │ └── package-info.md │ └── vm/ │ ├── java/ │ │ ├── bo.java.vm │ │ ├── controller.java.vm │ │ ├── domain.java.vm │ │ ├── mapper.java.vm │ │ ├── service.java.vm │ │ ├── serviceImpl.java.vm │ │ ├── sub-domain.java.vm │ │ └── vo.java.vm │ ├── js/ │ │ └── api.js.vm │ ├── sql/ │ │ ├── oracle/ │ │ │ └── sql.vm │ │ ├── postgres/ │ │ │ └── sql.vm │ │ ├── sql.vm │ │ └── sqlserver/ │ │ └── sql.vm │ ├── vue/ │ │ ├── index-tree.vue.vm │ │ ├── index.vue.vm │ │ └── v3/ │ │ ├── index-tree.vue.vm │ │ ├── index.vue.vm │ │ └── readme.txt │ └── xml/ │ └── mapper.xml.vm ├── ruoyi-job/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── top/ │ └── flya/ │ └── job/ │ ├── config/ │ │ ├── XxlJobConfig.java │ │ └── properties/ │ │ └── XxlJobProperties.java │ └── service/ │ └── SampleService.java ├── ruoyi-oss/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── top/ │ └── flya/ │ └── oss/ │ ├── constant/ │ │ └── OssConstant.java │ ├── core/ │ │ └── OssClient.java │ ├── entity/ │ │ └── UploadResult.java │ ├── enumd/ │ │ ├── AccessPolicyType.java │ │ └── PolicyType.java │ ├── exception/ │ │ └── OssException.java │ ├── factory/ │ │ └── OssFactory.java │ └── properties/ │ └── OssProperties.java ├── ruoyi-sms/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── top/ │ └── flya/ │ └── sms/ │ ├── config/ │ │ ├── SmsConfig.java │ │ └── properties/ │ │ └── SmsProperties.java │ ├── core/ │ │ ├── AliyunSmsTemplate.java │ │ ├── SmsTemplate.java │ │ └── TencentSmsTemplate.java │ ├── entity/ │ │ └── SmsResult.java │ └── exception/ │ └── SmsException.java ├── ruoyi-system/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── top/ │ │ └── flya/ │ │ └── system/ │ │ ├── domain/ │ │ │ ├── SysCache.java │ │ │ ├── SysConfig.java │ │ │ ├── SysLogininfor.java │ │ │ ├── SysNotice.java │ │ │ ├── SysOperLog.java │ │ │ ├── SysOss.java │ │ │ ├── SysOssConfig.java │ │ │ ├── SysPost.java │ │ │ ├── SysRoleDept.java │ │ │ ├── SysRoleMenu.java │ │ │ ├── SysUserOnline.java │ │ │ ├── SysUserPost.java │ │ │ ├── SysUserRole.java │ │ │ ├── bo/ │ │ │ │ ├── SysOssBo.java │ │ │ │ └── SysOssConfigBo.java │ │ │ └── vo/ │ │ │ ├── MetaVo.java │ │ │ ├── RouterVo.java │ │ │ ├── SysOssConfigVo.java │ │ │ ├── SysOssVo.java │ │ │ ├── SysUserExportVo.java │ │ │ └── SysUserImportVo.java │ │ ├── listener/ │ │ │ └── SysUserImportListener.java │ │ ├── mapper/ │ │ │ ├── SysConfigMapper.java │ │ │ ├── SysDeptMapper.java │ │ │ ├── SysDictDataMapper.java │ │ │ ├── SysDictTypeMapper.java │ │ │ ├── SysLogininforMapper.java │ │ │ ├── SysMenuMapper.java │ │ │ ├── SysNoticeMapper.java │ │ │ ├── SysOperLogMapper.java │ │ │ ├── SysOssConfigMapper.java │ │ │ ├── SysOssMapper.java │ │ │ ├── SysPostMapper.java │ │ │ ├── SysRoleDeptMapper.java │ │ │ ├── SysRoleMapper.java │ │ │ ├── SysRoleMenuMapper.java │ │ │ ├── SysUserMapper.java │ │ │ ├── SysUserPostMapper.java │ │ │ └── SysUserRoleMapper.java │ │ ├── runner/ │ │ │ └── SystemApplicationRunner.java │ │ └── service/ │ │ ├── ISysConfigService.java │ │ ├── ISysDataScopeService.java │ │ ├── ISysDeptService.java │ │ ├── ISysDictDataService.java │ │ ├── ISysDictTypeService.java │ │ ├── ISysLogininforService.java │ │ ├── ISysMenuService.java │ │ ├── ISysNoticeService.java │ │ ├── ISysOperLogService.java │ │ ├── ISysOssConfigService.java │ │ ├── ISysOssService.java │ │ ├── ISysPostService.java │ │ ├── ISysRoleService.java │ │ ├── ISysUserService.java │ │ ├── SysLoginService.java │ │ ├── SysPermissionService.java │ │ ├── SysRegisterService.java │ │ └── impl/ │ │ ├── SysConfigServiceImpl.java │ │ ├── SysDataScopeServiceImpl.java │ │ ├── SysDeptServiceImpl.java │ │ ├── SysDictDataServiceImpl.java │ │ ├── SysDictTypeServiceImpl.java │ │ ├── SysLogininforServiceImpl.java │ │ ├── SysMenuServiceImpl.java │ │ ├── SysNoticeServiceImpl.java │ │ ├── SysOperLogServiceImpl.java │ │ ├── SysOssConfigServiceImpl.java │ │ ├── SysOssServiceImpl.java │ │ ├── SysPostServiceImpl.java │ │ ├── SysRoleServiceImpl.java │ │ ├── SysSensitiveServiceImpl.java │ │ └── SysUserServiceImpl.java │ └── resources/ │ └── mapper/ │ ├── package-info.md │ └── system/ │ ├── SysConfigMapper.xml │ ├── SysDeptMapper.xml │ ├── SysDictDataMapper.xml │ ├── SysDictTypeMapper.xml │ ├── SysLogininforMapper.xml │ ├── SysMenuMapper.xml │ ├── SysNoticeMapper.xml │ ├── SysOperLogMapper.xml │ ├── SysOssConfigMapper.xml │ ├── SysOssMapper.xml │ ├── SysPostMapper.xml │ ├── SysRoleDeptMapper.xml │ ├── SysRoleMapper.xml │ ├── SysRoleMenuMapper.xml │ ├── SysUserMapper.xml │ ├── SysUserPostMapper.xml │ └── SysUserRoleMapper.xml ├── ruoyi-ui/ │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── babel.config.js │ ├── bin/ │ │ ├── build.bat │ │ ├── package.bat │ │ └── run-web.bat │ ├── build/ │ │ └── index.js │ ├── nginx.conf │ ├── package.json │ ├── public/ │ │ ├── html/ │ │ │ └── ie.html │ │ ├── index.html │ │ └── robots.txt │ ├── src/ │ │ ├── App.vue │ │ ├── api/ │ │ │ ├── demo/ │ │ │ │ ├── demo.js │ │ │ │ └── tree.js │ │ │ ├── login.js │ │ │ ├── menu.js │ │ │ ├── monitor/ │ │ │ │ ├── cache.js │ │ │ │ ├── logininfor.js │ │ │ │ ├── online.js │ │ │ │ └── operlog.js │ │ │ ├── system/ │ │ │ │ ├── activity.js │ │ │ │ ├── activityConnArtist.js │ │ │ │ ├── activityConnIntro.js │ │ │ │ ├── activityConnTag.js │ │ │ │ ├── activityGroup.js │ │ │ │ ├── activityGroupApply.js │ │ │ │ ├── artist.js │ │ │ │ ├── config.js │ │ │ │ ├── dept.js │ │ │ │ ├── dict/ │ │ │ │ │ ├── data.js │ │ │ │ │ └── type.js │ │ │ │ ├── intro.js │ │ │ │ ├── menu.js │ │ │ │ ├── notice.js │ │ │ │ ├── official.js │ │ │ │ ├── organizer.js │ │ │ │ ├── organizerTicket.js │ │ │ │ ├── oss.js │ │ │ │ ├── ossConfig.js │ │ │ │ ├── post.js │ │ │ │ ├── pzc_order.js │ │ │ │ ├── pzc_user.js │ │ │ │ ├── region.js │ │ │ │ ├── role.js │ │ │ │ ├── tag.js │ │ │ │ ├── user.js │ │ │ │ ├── userCollect.js │ │ │ │ ├── userHistory.js │ │ │ │ ├── userPhoto.js │ │ │ │ ├── userTalk.js │ │ │ │ └── viewPager.js │ │ │ └── tool/ │ │ │ └── gen.js │ │ ├── assets/ │ │ │ ├── icons/ │ │ │ │ ├── index.js │ │ │ │ └── svgo.yml │ │ │ └── styles/ │ │ │ ├── btn.scss │ │ │ ├── element-ui.scss │ │ │ ├── element-variables.scss │ │ │ ├── index.scss │ │ │ ├── mixin.scss │ │ │ ├── ruoyi.scss │ │ │ ├── sidebar.scss │ │ │ ├── transition.scss │ │ │ └── variables.scss │ │ ├── components/ │ │ │ ├── Breadcrumb/ │ │ │ │ └── index.vue │ │ │ ├── Crontab/ │ │ │ │ ├── day.vue │ │ │ │ ├── hour.vue │ │ │ │ ├── index.vue │ │ │ │ ├── min.vue │ │ │ │ ├── month.vue │ │ │ │ ├── result.vue │ │ │ │ ├── second.vue │ │ │ │ ├── week.vue │ │ │ │ └── year.vue │ │ │ ├── DictData/ │ │ │ │ └── index.js │ │ │ ├── DictTag/ │ │ │ │ └── index.vue │ │ │ ├── Editor/ │ │ │ │ └── index.vue │ │ │ ├── FileUpload/ │ │ │ │ └── index.vue │ │ │ ├── Hamburger/ │ │ │ │ └── index.vue │ │ │ ├── HeaderSearch/ │ │ │ │ └── index.vue │ │ │ ├── IconSelect/ │ │ │ │ ├── index.vue │ │ │ │ └── requireIcons.js │ │ │ ├── ImagePreview/ │ │ │ │ └── index.vue │ │ │ ├── ImageUpload/ │ │ │ │ └── index.vue │ │ │ ├── Pagination/ │ │ │ │ └── index.vue │ │ │ ├── PanThumb/ │ │ │ │ └── index.vue │ │ │ ├── ParentView/ │ │ │ │ └── index.vue │ │ │ ├── RightPanel/ │ │ │ │ └── index.vue │ │ │ ├── RightToolbar/ │ │ │ │ └── index.vue │ │ │ ├── RuoYi/ │ │ │ │ ├── Doc/ │ │ │ │ │ └── index.vue │ │ │ │ └── Git/ │ │ │ │ └── index.vue │ │ │ ├── Screenfull/ │ │ │ │ └── index.vue │ │ │ ├── SizeSelect/ │ │ │ │ └── index.vue │ │ │ ├── SvgIcon/ │ │ │ │ └── index.vue │ │ │ ├── ThemePicker/ │ │ │ │ └── index.vue │ │ │ ├── TopNav/ │ │ │ │ └── index.vue │ │ │ └── iFrame/ │ │ │ └── index.vue │ │ ├── directive/ │ │ │ ├── dialog/ │ │ │ │ ├── drag.js │ │ │ │ ├── dragHeight.js │ │ │ │ └── dragWidth.js │ │ │ ├── index.js │ │ │ ├── module/ │ │ │ │ └── clipboard.js │ │ │ └── permission/ │ │ │ ├── hasPermi.js │ │ │ └── hasRole.js │ │ ├── layout/ │ │ │ ├── components/ │ │ │ │ ├── AppMain.vue │ │ │ │ ├── IframeToggle/ │ │ │ │ │ └── index.vue │ │ │ │ ├── InnerLink/ │ │ │ │ │ └── index.vue │ │ │ │ ├── Navbar.vue │ │ │ │ ├── Settings/ │ │ │ │ │ └── index.vue │ │ │ │ ├── Sidebar/ │ │ │ │ │ ├── FixiOSBug.js │ │ │ │ │ ├── Item.vue │ │ │ │ │ ├── Link.vue │ │ │ │ │ ├── Logo.vue │ │ │ │ │ ├── SidebarItem.vue │ │ │ │ │ └── index.vue │ │ │ │ ├── TagsView/ │ │ │ │ │ ├── ScrollPane.vue │ │ │ │ │ └── index.vue │ │ │ │ └── index.js │ │ │ ├── index.vue │ │ │ └── mixin/ │ │ │ └── ResizeHandler.js │ │ ├── main.js │ │ ├── permission.js │ │ ├── plugins/ │ │ │ ├── auth.js │ │ │ ├── cache.js │ │ │ ├── download.js │ │ │ ├── index.js │ │ │ ├── modal.js │ │ │ └── tab.js │ │ ├── router/ │ │ │ └── index.js │ │ ├── settings.js │ │ ├── store/ │ │ │ ├── getters.js │ │ │ ├── index.js │ │ │ └── modules/ │ │ │ ├── app.js │ │ │ ├── dict.js │ │ │ ├── permission.js │ │ │ ├── settings.js │ │ │ ├── tagsView.js │ │ │ └── user.js │ │ ├── utils/ │ │ │ ├── auth.js │ │ │ ├── dict/ │ │ │ │ ├── Dict.js │ │ │ │ ├── DictConverter.js │ │ │ │ ├── DictData.js │ │ │ │ ├── DictMeta.js │ │ │ │ ├── DictOptions.js │ │ │ │ └── index.js │ │ │ ├── errorCode.js │ │ │ ├── generator/ │ │ │ │ ├── config.js │ │ │ │ ├── css.js │ │ │ │ ├── drawingDefault.js │ │ │ │ ├── html.js │ │ │ │ ├── icon.json │ │ │ │ ├── js.js │ │ │ │ └── render.js │ │ │ ├── index.js │ │ │ ├── jsencrypt.js │ │ │ ├── permission.js │ │ │ ├── request.js │ │ │ ├── ruoyi.js │ │ │ ├── scroll-to.js │ │ │ └── validate.js │ │ └── views/ │ │ ├── components/ │ │ │ └── icons/ │ │ │ ├── element-icons.js │ │ │ ├── index.vue │ │ │ └── svg-icons.js │ │ ├── dashboard/ │ │ │ ├── BarChart.vue │ │ │ ├── LineChart.vue │ │ │ ├── PanelGroup.vue │ │ │ ├── PieChart.vue │ │ │ ├── RaddarChart.vue │ │ │ └── mixins/ │ │ │ └── resize.js │ │ ├── demo/ │ │ │ ├── demo/ │ │ │ │ └── index.vue │ │ │ └── tree/ │ │ │ └── index.vue │ │ ├── error/ │ │ │ ├── 401.vue │ │ │ └── 404.vue │ │ ├── index.vue │ │ ├── index_v1.vue │ │ ├── login.vue │ │ ├── monitor/ │ │ │ ├── admin/ │ │ │ │ └── index.vue │ │ │ ├── cache/ │ │ │ │ ├── index.vue │ │ │ │ └── list.vue │ │ │ ├── logininfor/ │ │ │ │ └── index.vue │ │ │ ├── online/ │ │ │ │ └── index.vue │ │ │ ├── operlog/ │ │ │ │ └── index.vue │ │ │ └── xxljob/ │ │ │ └── index.vue │ │ ├── redirect.vue │ │ ├── register.vue │ │ ├── system/ │ │ │ ├── activity/ │ │ │ │ ├── add/ │ │ │ │ │ └── index.vue │ │ │ │ └── index.vue │ │ │ ├── activityConnArtist/ │ │ │ │ └── index.vue │ │ │ ├── activityConnIntro/ │ │ │ │ └── index.vue │ │ │ ├── activityConnTag/ │ │ │ │ └── index.vue │ │ │ ├── activityGroup/ │ │ │ │ └── index.vue │ │ │ ├── activityGroupApply/ │ │ │ │ └── index.vue │ │ │ ├── artist/ │ │ │ │ └── index.vue │ │ │ ├── config/ │ │ │ │ └── index.vue │ │ │ ├── dept/ │ │ │ │ └── index.vue │ │ │ ├── dict/ │ │ │ │ ├── data.vue │ │ │ │ └── index.vue │ │ │ ├── intro/ │ │ │ │ └── index.vue │ │ │ ├── menu/ │ │ │ │ └── index.vue │ │ │ ├── notice/ │ │ │ │ └── index.vue │ │ │ ├── official/ │ │ │ │ └── index.vue │ │ │ ├── organizer/ │ │ │ │ └── index.vue │ │ │ ├── organizerTicket/ │ │ │ │ └── index.vue │ │ │ ├── oss/ │ │ │ │ ├── config.vue │ │ │ │ └── index.vue │ │ │ ├── post/ │ │ │ │ └── index.vue │ │ │ ├── pzc_order/ │ │ │ │ └── index.vue │ │ │ ├── pzc_user/ │ │ │ │ ├── index.vue │ │ │ │ └── todoList/ │ │ │ │ └── index.vue │ │ │ ├── region/ │ │ │ │ └── index.vue │ │ │ ├── role/ │ │ │ │ ├── authUser.vue │ │ │ │ ├── index.vue │ │ │ │ └── selectUser.vue │ │ │ ├── tag/ │ │ │ │ └── index.vue │ │ │ ├── user/ │ │ │ │ ├── authRole.vue │ │ │ │ ├── index.vue │ │ │ │ └── profile/ │ │ │ │ ├── index.vue │ │ │ │ ├── resetPwd.vue │ │ │ │ ├── userAvatar.vue │ │ │ │ └── userInfo.vue │ │ │ ├── userCollect/ │ │ │ │ └── index.vue │ │ │ ├── userHistory/ │ │ │ │ └── index.vue │ │ │ ├── userPhoto/ │ │ │ │ └── index.vue │ │ │ ├── userTalk/ │ │ │ │ └── index.vue │ │ │ └── viewPager/ │ │ │ └── index.vue │ │ └── tool/ │ │ ├── build/ │ │ │ ├── CodeTypeDialog.vue │ │ │ ├── DraggableItem.vue │ │ │ ├── IconsDialog.vue │ │ │ ├── RightPanel.vue │ │ │ ├── TreeNodeDialog.vue │ │ │ └── index.vue │ │ └── gen/ │ │ ├── basicInfoForm.vue │ │ ├── editTable.vue │ │ ├── genInfoForm.vue │ │ ├── importTable.vue │ │ └── index.vue │ └── vue.config.js └── script/ ├── bin/ │ ├── ry.bat │ └── ry.sh ├── docker/ │ ├── database.yml │ ├── docker-compose.yml │ ├── nginx/ │ │ └── conf/ │ │ └── nginx.conf │ └── redis/ │ ├── conf/ │ │ └── redis.conf │ └── data/ │ └── README.md └── sql/ ├── oracle/ │ ├── oracle_ry_vue_4.X.sql │ └── oracle_test.sql ├── postgres/ │ ├── postgres_ry_vue_4.X.sql │ └── postgres_test.sql ├── ry_vue_4.X.sql ├── sqlserver/ │ ├── sqlserver_ry_vue_4.X.sql │ └── sqlserver_test.sql ├── tables_xxl_job.sql ├── test.sql └── update/ ├── oracle/ │ ├── update-4.1-4.2.sql │ ├── update-4.2-4.3.sql │ └── update-4.3-4.4.sql ├── postgres/ │ ├── update-4.1-4.2.sql │ ├── update-4.2-4.3.sql │ └── update-4.3-4.4.sql ├── sqlserver/ │ ├── update-4.1-4.2.sql │ ├── update-4.2-4.3.sql │ └── update-4.3-4.4.sql ├── update-3.X-4.0.sql ├── update-4.0-4.1.sql ├── update-4.1-4.2.sql ├── update-4.2-4.3.sql └── update-4.3-4.4.sql ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # http://editorconfig.org root = true # 空格替代Tab缩进在各种编辑工具下效果一致 [*] indent_style = space indent_size = 4 charset = utf-8 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true [*.{json,yml,yaml}] indent_size = 2 [*.md] insert_final_newline = false trim_trailing_whitespace = false ================================================ FILE: .gitee/ISSUE_TEMPLATE.zh-CN.md ================================================ ### 使用版本(未按照模板填写直接删除) - jdk版本(带上尾号): 例如 1.8.0_202 - 框架版本(项目启动时输出的版本号): 例如 4.4.0 - 其他依赖版本(你觉得有必要的): ### 问题前提 > 功能不好用 不会用 是否已经看过项目文档 > 项目运行报错 是否已经拿着报错信息去百度 常见报错百度百度足以 > 是否搜索过其他issue 一些已经解决的问题 会在issue内留下解决方法 > 无法线上解决或者与框架无关的问题的欢迎加VIP群跟作者一对一谈 ### 异常模块 > 此报错都涉及到那些系统模块 例如 ruoyi-system ruoyi-auth 等等 ### 问题描述 > 越详细越容易直击问题所在 已知: XXX功能不好用 或 XXX数据不正常 等等 ### 希望结果 > 想知道你觉得怎么样是正常或者合理的 希望功能可以有XXX结果 或者 XXX现象 ### 重现步骤 > 作者并不知道这个问题是如何出现的 - 1 - 2 - 3 ### 相关代码与报错信息(请勿发混乱格式) > 代码可按照如下形式提供或者截图均可 越详细越好 > 大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题 ```java public class XXX { } ``` ================================================ FILE: .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md ================================================ ### 更改目的 解决了什么问题(请提交到dev分支) ### 改动逻辑 这么写的思路(让作者更好的理解你的意图) ### 测试 都做了哪些测试(未经过测试不采纳) ================================================ FILE: .gitignore ================================================ ###################################################################### # Build Tools .gradle /build/ !gradle/wrapper/gradle-wrapper.jar target/ !.mvn/wrapper/maven-wrapper.jar ###################################################################### # IDE ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### JRebel ### rebel.xml ### NetBeans ### nbproject/private/ build/* nbbuild/ nbdist/ .nb-gradle/ ###################################################################### # Others *.log *.xml.versionsBackup *.swp !*/build/*.java !*/build/*.html !*/build/*.xml ================================================ FILE: .run/ruoyi-monitor-admin.run.xml ================================================ ================================================ FILE: .run/ruoyi-server.run.xml ================================================ ================================================ FILE: .run/ruoyi-xxl-job-admin.run.xml ================================================ ================================================ FILE: .vscode/settings.json ================================================ { "java.compile.nullAnalysis.mode": "automatic" } ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: PaiZhiCheng/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 pai-zhi-cheng org.apache.commons commons-jexl3 3.2.1 com.github.ismail-mekni mxreflection 1.0.1 com.corundumstudio.socketio netty-socketio 1.7.16 com.ruoyi ruoyi-common com.ruoyi ruoyi-system com.alibaba fastjson 2.0.10 io.undertow undertow-core com.xuxueli xxl-job-core com.github.wechatpay-apiv3 wechatpay-apache-httpclient 0.4.7 com.squareup.okhttp3 okhttp 4.4.1 ================================================ FILE: PaiZhiCheng/src/main/java/Main.java ================================================ import lombok.SneakyThrows; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; import static org.apache.xmlbeans.impl.schema.StscState.start; public class Main { public static final int max = 100; public static int count = 0; public static Semaphore semaphoreA =new Semaphore(1); public static Semaphore semaphoreB =new Semaphore(0); @SneakyThrows public static void main(String[] args) throws InterruptedException { CompletableFuture.runAsync(()->{ for (int i = 0; i < max/2; i++) { try { semaphoreB.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("B"); count++; semaphoreA.release(); } }); CompletableFuture.runAsync(()->{ for (int i = 0; i < max/2; i++) { try { semaphoreA.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("A"); count++; semaphoreB.release(); } }); Thread.sleep(1000); System.out.println("count = " + count); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/common/BatchUtils.java ================================================ package top.flya.system.common; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import top.flya.system.domain.vo.*; import top.flya.system.service.ISysOssService; import javax.annotation.Resource; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @Component @Slf4j public class BatchUtils { @Resource private ISysOssService iSysOssService; // 假设这里有一个方法可以批量查询新的 imageUrl public Map getNewImageUrls(List imageUrls) { // List ossIds = imageUrls.stream().map(Long::parseLong).collect(Collectors.toList()); List ossIds = imageUrls.stream() .map(url -> { try { return Long.parseLong(url); } catch (NumberFormatException e) { // Handle the exception, e.g. logging or skipping the invalid value // You can also return a default value in case of invalid format return null; } }) .filter(Objects::nonNull) .collect(Collectors.toList()); return iSysOssService.listByIds(ossIds).stream().collect(Collectors.toMap(SysOssVo::getOssId, SysOssVo::getUrl)); } public List transformToPzcArtistVo(List artistList) { log.info("transform artistList start: {}", artistList); // 获取所有旧的 imageUrl List oldImageUrls = artistList.stream() .map(PzcArtistVo::getImageUrl) .collect(Collectors.toList()); // 批量查询新的 imageUrl Map newImageUrls = getNewImageUrls(oldImageUrls); // 使用 Stream API 进行处理 return artistList.stream() // 对列表中的每个元素进行处理 .map(artist -> { // 从 Map 中获取新的 imageUrl String newImageUrl = artist.getImageUrl().contains("http")?artist.getImageUrl():newImageUrls.get(Long.parseLong(artist.getImageUrl())); // 创建一个新的 PzcArtistVo 对象,使用查询到的新 imageUrl return new PzcArtistVo( artist.getArtistId(), artist.getName(), newImageUrl, artist.getDescription() ); }) // 将处理后的元素收集到一个新的 List 中 .collect(Collectors.toList()); } public List transformToPzcOrganizerVo(List organizerList) { log.info("transform organizerList start: {}", organizerList); // 获取所有旧的 imageUrl List oldImageUrls = organizerList.stream() .map(PzcOrganizerVo::getLogo) .collect(Collectors.toList()); // 批量查询新的 imageUrl Map newImageUrls = getNewImageUrls(oldImageUrls); // 使用 Stream API 进行处理 return organizerList.stream() // 对列表中的每个元素进行处理 .map(organizer -> { if(organizer.getLogo()==null){ return organizer; } // 从 Map 中获取新的 imageUrl String newImageUrl = organizer.getLogo().contains("http")?organizer.getLogo():newImageUrls.get(Long.parseLong(organizer.getLogo())); // 创建一个新的 PzcArtistVo 对象,使用查询到的新 imageUrl return new PzcOrganizerVo( organizer.getOrganizerId(), organizer.getPhone(), organizer.getName(), newImageUrl, organizer.getContent(), organizer.getCreateTime(), organizer.getUpdateTime() ); }) // 将处理后的元素收集到一个新的 List 中 .collect(Collectors.toList()); } public List transformToPzcTagVo(List tagList) { log.info("transform tagList start: {}", tagList); // 获取所有旧的 imageUrl List oldImageUrls = tagList.stream() .map(PzcTagVo::getImageUrl) .collect(Collectors.toList()); // 批量查询新的 imageUrl Map newImageUrls = getNewImageUrls(oldImageUrls); // 使用 Stream API 进行处理 return tagList.stream() // 对列表中的每个元素进行处理 .map(tag -> { // 从 Map 中获取新的 imageUrl String newImageUrl = tag.getImageUrl().contains("http")?tag.getImageUrl():newImageUrls.get(Long.parseLong(tag.getImageUrl())); // 创建一个新的 PzcArtistVo 对象,使用查询到的新 imageUrl return new PzcTagVo( tag.getTagId(), tag.getName(), newImageUrl, tag.getCreateTime(), tag.getUpdateTime() ); }) // 将处理后的元素收集到一个新的 List 中 .collect(Collectors.toList()); } public List transformToPzcIntroVo(List introList){ log.info("transform introList start: {}", JSONUtil.toJsonPrettyStr(introList)); // 获取所有旧的 imageUrl List oldImageUrls = introList.stream() .map(PzcIntroVo::getImageFullUrl) .collect(Collectors.toList()); // 批量查询新的 imageUrl Map newImageUrls = getNewImageUrls(oldImageUrls); // 使用 Stream API 进行处理 return introList.stream() // 对列表中的每个元素进行处理 .map(intro -> { // 从 Map 中获取新的 imageUrl String newImageUrl = intro.getImageFullUrl().contains("http")?intro.getImageFullUrl():newImageUrls.get(Long.parseLong(intro.getImageFullUrl())); // 创建一个新的 PzcArtistVo 对象,使用查询到的新 imageUrl return new PzcIntroVo( intro.getIntroId(), intro.getTitle(), intro.getContent(), intro.getType(), newImageUrl, intro.getCreateTime(), intro.getUpdateTime() ); }) // 将处理后的元素收集到一个新的 List 中 .collect(Collectors.toList()); } public List transformToPzcUserVo(List userList){ log.info("transform userList start: {}", JSONUtil.toJsonPrettyStr(userList)); // 获取所有旧的 imageUrl List oldImageUrls = userList.stream() .filter(user -> user.getAvatar() != null&&!user.getAvatar().contains("http")) .map(PzcUserVo::getAvatar) .collect(Collectors.toList()); // 批量查询新的 imageUrl Map newImageUrls = getNewImageUrls(oldImageUrls); // 使用 Stream API 进行处理 return userList.stream() // 对列表中的每个元素进行处理 .map(user -> { // 从 Map 中获取新的 imageUrl if(user.getAvatar()!=null&&!user.getAvatar().contains("http")) //如果是http开头的就不用转换了 { String newImageUrl = newImageUrls.get(Long.parseLong(user.getAvatar())); // 创建一个新的 PzcArtistVo 对象,使用查询到的新 imageUrl return new PzcUserVo( user.getUserId(), user.getOpenid(), user.getMoney(), user.getUserLevel(), user.getIntegration(), user.getIntegrationNow(), user.getRealname(), user.getNickname(), user.getSex(), user.getPhone(), newImageUrl, user.getAddress(), user.getIntro(), user.getAge(), user.getConstellation(), user.getMbti(), user.getHobby(), user.getSchool(), user.getOccupation(), user.getCreateTime(), user.getUpdateTime(), user.getMusicStyle(), user.getState(), user.getExemptCancel() ); } return user; }) // 将处理后的元素收集到一个新的 List 中 .collect(Collectors.toList()); } public List transformToPzcViewPagerVo(List viewPagerList) { // log.info("transform viewPagerList start: {}", JSONUtil.toJsonPrettyStr(viewPagerList)); // 获取所有旧的 imageUrl List oldImageUrls = viewPagerList.stream() .map(PzcViewPagerVo::getImageUrl) .collect(Collectors.toList()); // 批量查询新的 imageUrl Map newImageUrls = getNewImageUrls(oldImageUrls); // 使用 Stream API 进行处理 return viewPagerList.stream() // 对列表中的每个元素进行处理 .map(viewPager -> { // 从 Map 中获取新的 imageUrl String newImageUrl = viewPager.getImageUrl().contains("http")?viewPager.getImageUrl():newImageUrls.get(Long.parseLong(viewPager.getImageUrl())); // 创建一个新的 PzcArtistVo 对象,使用查询到的新 imageUrl return new PzcViewPagerVo( viewPager.getViewPagerId(), viewPager.getName(), newImageUrl, viewPager.getLinkUrl(), viewPager.getState(), viewPager.getActivityId() ); }) // 将处理后的元素收集到一个新的 List 中 .collect(Collectors.toList()); } public List transformToPzcActivityVo(List records) { log.info("transform activityList start: {}", JSONUtil.toJsonPrettyStr(records)); // 获取所有旧的 imageUrl List oldImageUrls = records.stream() .map(PzcActivityVo::getCoverImage) .collect(Collectors.toList()); List innerImageUrls = records.stream() .map(PzcActivityVo::getInnerImage) .collect(Collectors.toList()); List shareImageUrls = records.stream() .filter(activity -> activity.getShareImage() != null) .map(PzcActivityVo::getShareImage) .collect(Collectors.toList()); // 批量查询新的 imageUrl Map newImageUrls = getNewImageUrls(oldImageUrls); Map newInnerImageUrls = getNewImageUrls(innerImageUrls); Map newShareImageUrls = getNewImageUrls(shareImageUrls); // 使用 Stream API 进行处理 return records.stream() .map( r->{ String newImageUrl = r.getCoverImage().contains("http")?r.getCoverImage():newImageUrls.get(Long.parseLong(r.getCoverImage())); String innerImage = r.getInnerImage().contains("http")?r.getInnerImage():newInnerImageUrls.get(Long.parseLong(r.getInnerImage())); String shareImage = r.getShareImage()==null?null:r.getShareImage().contains("http")?r.getShareImage():newShareImageUrls.get(Long.parseLong(r.getShareImage())); return new PzcActivityVo( r.getActivityId(), r.getAddress(), r.getRegionId(), r.getTitle(), r.getStartTime(), r.getEndDate(), innerImage, r.getShowTime(), newImageUrl, r.getCreateTime(), r.getUpdateTime(), r.getState(), r.getOrganizerId(), r.getClassify(), r.getRegion(), null, null, null, null, null, shareImage ); } ).collect(Collectors.toList()); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/common/CheckUtils.java ================================================ package top.flya.system.common; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import top.flya.common.core.domain.R; import top.flya.common.utils.StringUtils; import top.flya.system.domain.PzcActivity; import top.flya.system.mapper.PzcActivityMapper; import javax.annotation.Resource; @Slf4j @Component public class CheckUtils { @Resource private PzcActivityMapper pzcActivityMapper; /** * 0 是创建活动 1是修改活动 * @param activity * @param type * @return */ public R checkCreateActivity(PzcActivity activity, Integer type) { log.info("checkActivity check init"); if(type==1) { if(activity.getActivityId()==null) { return R.fail("活动id不能为空"); } PzcActivity checkActivity = pzcActivityMapper.selectById(activity.getActivityId()); if(checkActivity==null) { return R.fail("活动不存在"); } } if(StringUtils.isEmpty(activity.getTitle())) { return R.fail("活动标题不能为空"); } if(StringUtils.isEmpty(activity.getStartTime())) { return R.fail("活动开始时间不能为空"); } if(StringUtils.isEmpty(activity.getEndDate())) { return R.fail("活动结束时间不能为空"); } if(StringUtils.isEmpty(activity.getCoverImage())) { return R.fail("活动封面不能为空"); } return R.ok(); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/config/ClientCache.java ================================================ package top.flya.system.config; import com.corundumstudio.socketio.SocketIOClient; import lombok.Data; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** * @author litong * @date 2019/11/6 16:01 */ @Component public class ClientCache { /** * 本地缓存 */ public static Map> concurrentHashMap = new ConcurrentHashMap<>(); public Map> getConcurrentHashMap() { return concurrentHashMap; } /** * 存入本地缓存 * * @param userId 用户ID * @param sessionId 页面sessionID * @param socketIOClient 页面对应的通道连接信息 */ public void saveClient(String userId, UUID sessionId, SocketIOClient socketIOClient) { HashMap sessionIdClientCache = concurrentHashMap.get(userId); if (sessionIdClientCache == null) { sessionIdClientCache = new HashMap<>(); } sessionIdClientCache.put(sessionId, socketIOClient); concurrentHashMap.put(userId, sessionIdClientCache); } /** * 根据用户ID获取所有通道信息 * * @param userId * @return */ public HashMap getUserClient(String userId) { return concurrentHashMap.get(userId); } /** * 根据用户ID及页面sessionID删除页面链接信息 * * @param userId * @param sessionId */ public void deleteSessionClient(String userId, UUID sessionId) { concurrentHashMap.remove(userId); // concurrentHashMap.get(userId).remove(sessionId); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/config/DbTemplate.java ================================================ package top.flya.system.config; import cn.hutool.core.collection.CollUtil; import org.springframework.stereotype.Component; import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** *

* 模拟数据库 *

* * @author yangkai.shen * @date Created in 2018-12-18 19:12 */ @Component public class DbTemplate { /** * 模拟数据库存储 user_id <-> session_id 的关系 */ public static final ConcurrentHashMap DB = new ConcurrentHashMap<>(); /** * 获取所有SessionId * * @return SessionId列表 */ public List findAll() { return CollUtil.newArrayList(DB.values()); } /** * 根据UserId查询SessionId * * @param userId 用户id * @return SessionId */ public Optional findByUserId(String userId) { return Optional.ofNullable(DB.get(userId)); } /** * 保存/更新 user_id <-> session_id 的关系 * * @param userId 用户id * @param sessionId SessionId */ public void save(String userId, UUID sessionId) { DB.put(userId, sessionId); } /** * 删除 user_id <-> session_id 的关系 * * @param userId 用户id */ public void deleteByUserId(String userId) { DB.remove(userId); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/config/Event.java ================================================ package top.flya.system.config; /** *

* 事件常量 *

* * @author yangkai.shen * @date Created in 2018-12-18 19:36 */ public interface Event { /** * 聊天事件 */ String CHAT = "chat"; /** * 官方消息事件 */ String OFFICIAL = "official"; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/config/ServerConfig.java ================================================ package top.flya.system.config; import com.corundumstudio.socketio.SocketConfig; import com.corundumstudio.socketio.SocketIOServer; import com.corundumstudio.socketio.Transport; import com.corundumstudio.socketio.annotation.SpringAnnotationScanner; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import top.flya.common.utils.JsonUtils; /** *

* websocket服务器配置 *

* * @author yangkai.shen * @date Created in 2018-12-18 16:42 */ @Configuration @Slf4j public class ServerConfig { @Bean public SocketIOServer server(WsConfig wsConfig) { log.info("init wsConfig: {}", JsonUtils.toJsonString(wsConfig)); com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); config.setHostname(wsConfig.getHost()); config.setPort(wsConfig.getPort()); config.setTransports(Transport.WEBSOCKET,Transport.POLLING); //test SocketConfig socketConfig = config.getSocketConfig(); socketConfig.setReuseAddress(true); //地址复用 //这个listener可以用来进行身份验证 // config.setAuthorizationListener(data -> { // // http://localhost:8081?token=xxxxxxx // // 例如果使用上面的链接进行connect,可以使用如下代码获取用户密码信息,本文不做身份验证 // String token = data.getSingleUrlParam("token"); // // 校验token的合法性,实际业务需要校验token是否过期等等,参考 spring-boot-demo-rbac-security 里的 JwtUtil // // 如果认证不通过会返回一个 Socket.EVENT_CONNECT_ERROR 事件 // return StrUtil.isNotBlank(token); // }); return new SocketIOServer(config); } /** * Spring 扫描自定义注解 */ @Bean public SpringAnnotationScanner springAnnotationScanner(SocketIOServer server) { return new SpringAnnotationScanner(server); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/config/ServerRunner.java ================================================ package top.flya.system.config; import com.corundumstudio.socketio.SocketIOServer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; /** *

* websocket服务器启动 *

* * @author yangkai.shen * @date Created in 2018-12-18 17:07 */ @Component @Slf4j public class ServerRunner implements CommandLineRunner { @Autowired private SocketIOServer server; @Override public void run(String... args) { server.start(); log.info("websocket 服务器启动成功"); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/config/WsConfig.java ================================================ package top.flya.system.config; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.stereotype.Component; import javax.validation.Valid; /** *

* WebSocket配置类 *

* * @author yangkai.shen * @date Created in 2018-12-18 16:41 */ @Data @Component public class WsConfig { /** * 端口号 */ @Value("${wx.server.port}") private Integer port ; /** * host */ @Value("${wx.server.host}") private String host ; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/GaoDeMapController.java ================================================ package top.flya.system.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import top.flya.common.core.domain.R; import top.flya.system.utils.gaode.GaoDeMapUtil; import javax.annotation.Resource; /** * @Description: 地图控制层 * @Author: isymikasan * @Date: 2022-01-26 09:36:55 */ @RestController @RequestMapping("/point") public class GaoDeMapController { @Resource private GaoDeMapUtil gaoDeMapUtil; public static final Logger log = LoggerFactory.getLogger(GaoDeMapController.class); @GetMapping("/getAddress") public R getAddress(@RequestParam("longitude") String longitude, @RequestParam("latitude") String latitude) { try { return gaoDeMapUtil.getAddress(longitude, latitude); } catch (Exception e) { return R.fail(e.toString()); } } @GetMapping("/getLonLat") public R getLonLat(@RequestParam("address") String address) { return gaoDeMapUtil.getLonLat(address); } @GetMapping("/getDistance") public R getDistance(@RequestParam("start") String startLonLat,@RequestParam("end") String endLonLat) { return gaoDeMapUtil.getDistance(startLonLat, endLonLat); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityConnArtistController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcActivityConnArtistVo; import top.flya.system.domain.bo.PzcActivityConnArtistBo; import top.flya.system.service.IPzcActivityConnArtistService; import top.flya.common.core.page.TableDataInfo; /** * 活动关联艺人 * * @author ruoyi * @date 2023-06-02 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/activityConnArtist") public class PzcActivityConnArtistController extends BaseController { private final IPzcActivityConnArtistService iPzcActivityConnArtistService; /** * 查询活动关联艺人列表 */ @SaCheckPermission("system:activityConnArtist:list") @GetMapping("/list") public TableDataInfo list(PzcActivityConnArtistBo bo, PageQuery pageQuery) { return iPzcActivityConnArtistService.queryPageList(bo, pageQuery); } /** * 导出活动关联艺人列表 */ @SaCheckPermission("system:activityConnArtist:export") @Log(title = "活动关联艺人", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcActivityConnArtistBo bo, HttpServletResponse response) { List list = iPzcActivityConnArtistService.queryList(bo); ExcelUtil.exportExcel(list, "活动关联艺人", PzcActivityConnArtistVo.class, response); } /** * 获取活动关联艺人详细信息 * * @param activityConnArtistId 主键 */ @SaCheckPermission("system:activityConnArtist:query") @GetMapping("/{activityConnArtistId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Integer activityConnArtistId) { return R.ok(iPzcActivityConnArtistService.queryById(activityConnArtistId)); } /** * 新增活动关联艺人 */ @SaCheckPermission("system:activityConnArtist:add") @Log(title = "活动关联艺人", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcActivityConnArtistBo bo) { return toAjax(iPzcActivityConnArtistService.insertByBo(bo)); } /** * 修改活动关联艺人 */ @SaCheckPermission("system:activityConnArtist:edit") @Log(title = "活动关联艺人", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcActivityConnArtistBo bo) { return toAjax(iPzcActivityConnArtistService.updateByBo(bo)); } /** * 删除活动关联艺人 * * @param activityConnArtistIds 主键串 */ @SaCheckPermission("system:activityConnArtist:remove") @Log(title = "活动关联艺人", businessType = BusinessType.DELETE) @DeleteMapping("/{activityConnArtistIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Integer[] activityConnArtistIds) { return toAjax(iPzcActivityConnArtistService.deleteWithValidByIds(Arrays.asList(activityConnArtistIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityConnIntroController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcActivityConnIntroVo; import top.flya.system.domain.bo.PzcActivityConnIntroBo; import top.flya.system.service.IPzcActivityConnIntroService; import top.flya.common.core.page.TableDataInfo; /** * 活动介绍与活动关联 * * @author ruoyi * @date 2023-06-02 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/activityConnIntro") public class PzcActivityConnIntroController extends BaseController { private final IPzcActivityConnIntroService iPzcActivityConnIntroService; /** * 查询活动介绍与活动关联列表 */ @SaCheckPermission("system:activityConnIntro:list") @GetMapping("/list") public TableDataInfo list(PzcActivityConnIntroBo bo, PageQuery pageQuery) { return iPzcActivityConnIntroService.queryPageList(bo, pageQuery); } /** * 导出活动介绍与活动关联列表 */ @SaCheckPermission("system:activityConnIntro:export") @Log(title = "活动介绍与活动关联", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcActivityConnIntroBo bo, HttpServletResponse response) { List list = iPzcActivityConnIntroService.queryList(bo); ExcelUtil.exportExcel(list, "活动介绍与活动关联", PzcActivityConnIntroVo.class, response); } /** * 获取活动介绍与活动关联详细信息 * * @param activityConnIntroId 主键 */ @SaCheckPermission("system:activityConnIntro:query") @GetMapping("/{activityConnIntroId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Integer activityConnIntroId) { return R.ok(iPzcActivityConnIntroService.queryById(activityConnIntroId)); } /** * 新增活动介绍与活动关联 */ @SaCheckPermission("system:activityConnIntro:add") @Log(title = "活动介绍与活动关联", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcActivityConnIntroBo bo) { return toAjax(iPzcActivityConnIntroService.insertByBo(bo)); } /** * 修改活动介绍与活动关联 */ @SaCheckPermission("system:activityConnIntro:edit") @Log(title = "活动介绍与活动关联", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcActivityConnIntroBo bo) { return toAjax(iPzcActivityConnIntroService.updateByBo(bo)); } /** * 删除活动介绍与活动关联 * * @param activityConnIntroIds 主键串 */ @SaCheckPermission("system:activityConnIntro:remove") @Log(title = "活动介绍与活动关联", businessType = BusinessType.DELETE) @DeleteMapping("/{activityConnIntroIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Integer[] activityConnIntroIds) { return toAjax(iPzcActivityConnIntroService.deleteWithValidByIds(Arrays.asList(activityConnIntroIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityConnTagController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcActivityConnTagVo; import top.flya.system.domain.bo.PzcActivityConnTagBo; import top.flya.system.service.IPzcActivityConnTagService; import top.flya.common.core.page.TableDataInfo; /** * 活动标签与活动关联 * * @author ruoyi * @date 2023-06-03 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/activityConnTag") public class PzcActivityConnTagController extends BaseController { private final IPzcActivityConnTagService iPzcActivityConnTagService; /** * 查询活动标签与活动关联列表 */ @SaCheckPermission("system:activityConnTag:list") @GetMapping("/list") public TableDataInfo list(PzcActivityConnTagBo bo, PageQuery pageQuery) { return iPzcActivityConnTagService.queryPageList(bo, pageQuery); } /** * 导出活动标签与活动关联列表 */ @SaCheckPermission("system:activityConnTag:export") @Log(title = "活动标签与活动关联", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcActivityConnTagBo bo, HttpServletResponse response) { List list = iPzcActivityConnTagService.queryList(bo); ExcelUtil.exportExcel(list, "活动标签与活动关联", PzcActivityConnTagVo.class, response); } /** * 获取活动标签与活动关联详细信息 * * @param activityConnTagId 主键 */ @SaCheckPermission("system:activityConnTag:query") @GetMapping("/{activityConnTagId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Integer activityConnTagId) { return R.ok(iPzcActivityConnTagService.queryById(activityConnTagId)); } /** * 新增活动标签与活动关联 */ @SaCheckPermission("system:activityConnTag:add") @Log(title = "活动标签与活动关联", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcActivityConnTagBo bo) { return toAjax(iPzcActivityConnTagService.insertByBo(bo)); } /** * 修改活动标签与活动关联 */ @SaCheckPermission("system:activityConnTag:edit") @Log(title = "活动标签与活动关联", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcActivityConnTagBo bo) { return toAjax(iPzcActivityConnTagService.updateByBo(bo)); } /** * 删除活动标签与活动关联 * * @param activityConnTagIds 主键串 */ @SaCheckPermission("system:activityConnTag:remove") @Log(title = "活动标签与活动关联", businessType = BusinessType.DELETE) @DeleteMapping("/{activityConnTagIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Integer[] activityConnTagIds) { return toAjax(iPzcActivityConnTagService.deleteWithValidByIds(Arrays.asList(activityConnTagIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.bo.PzcActivityBo; import top.flya.system.domain.vo.PzcActivityVo; import top.flya.system.service.IPzcActivityService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; /** * 活动 * * @author ruoyi * @date 2023-06-02 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/activity") @Slf4j public class PzcActivityController extends BaseController { private final IPzcActivityService iPzcActivityService; /** * 新增活动 */ @SaCheckPermission("system:activity:add") @Log(title = "活动", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcActivityBo bo) { return toAjax(iPzcActivityService.insertByBo(bo)); } /** * 修改活动 */ @SaCheckPermission("system:activity:edit") @Log(title = "活动", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcActivityBo bo) { return toAjax(iPzcActivityService.updateByBo(bo)); } /** * 查询活动列表 */ @SaCheckPermission("system:activity:list") @GetMapping("/list") public TableDataInfo list(PzcActivityBo bo, PageQuery pageQuery) { return iPzcActivityService.queryPageList(bo, pageQuery); } /** * 查询活动列表 小程序端 */ @GetMapping("/listWx") public TableDataInfo Wx(PzcActivityBo bo, PageQuery pageQuery) { pageQuery.setIsAsc("desc"); pageQuery.setOrderByColumn("start_time"); return iPzcActivityService.queryPageListWx(bo, pageQuery); } /** * 导出活动列表 */ @SaCheckPermission("system:activity:export") @Log(title = "活动", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcActivityBo bo, HttpServletResponse response) { List list = iPzcActivityService.queryList(bo); ExcelUtil.exportExcel(list, "活动", PzcActivityVo.class, response); } /** * 获取活动详细信息 * * @param activityId 主键 */ @GetMapping("/{activityId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Integer activityId) { return R.ok(iPzcActivityService.queryById(activityId)); } /** * 删除活动 * * @param activityIds 主键串 */ @SaCheckPermission("system:activity:remove") @Log(title = "活动", businessType = BusinessType.DELETE) @DeleteMapping("/{activityIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Integer[] activityIds) { return toAjax(iPzcActivityService.deleteWithValidByIds(Arrays.asList(activityIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityGroupApplyController.java ================================================ package top.flya.system.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.PzcActivityGroup; import top.flya.system.domain.PzcActivityGroupApply; import top.flya.system.domain.PzcUser; import top.flya.system.domain.bo.PzcActivityGroupApplyBo; import top.flya.system.domain.bo.WxzApplyBo; import top.flya.system.domain.vo.PzcActivityGroupApplyVo; import top.flya.system.mapper.PzcActivityGroupApplyMapper; import top.flya.system.mapper.PzcActivityGroupMapper; import top.flya.system.mapper.PzcUserMapper; import top.flya.system.service.IPzcActivityGroupApplyService; import top.flya.system.utils.ActivityUtils; import top.flya.system.utils.WxUtils; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * 活动组队申请列表 * * @author ruoyi * @date 2023-07-10 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/activityGroupApply") @Slf4j public class PzcActivityGroupApplyController extends BaseController { private final IPzcActivityGroupApplyService iPzcActivityGroupApplyService; private final ActivityUtils activityUtils; private final PzcActivityGroupApplyMapper pzcActivityGroupApplyMapper; private final PzcUserMapper pzcUserMapper; private final PzcActivityGroupMapper pzcActivityGroupMapper; private final StringRedisTemplate stringRedisTemplate; private final WxUtils wxUtils; private final ExecutorService newSingleThreadExecutor = new ThreadPoolExecutor(10, 20, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(100)); @PostMapping("/wxzApply") @Transactional public R wxzApply(@RequestParam("applyId") Integer applyId, @RequestParam("wxz") Integer wxz) { //wxz 0 未选择 1 选择 log.info("applyId:{},wxz:{}", applyId, wxz); if (wxz != 0 && wxz != 1) { return R.fail("参数错误"); } PzcActivityGroupApply pzcActivityGroupApply = pzcActivityGroupApplyMapper.selectById(applyId); if (pzcActivityGroupApply == null) { return R.fail("申请不存在"); } if (pzcActivityGroupApply.getApplyStatus() != 2 && pzcActivityGroupApply.getApplyStatus() != 9 && pzcActivityGroupApply.getApplyStatus() != 10 && pzcActivityGroupApply.getApplyStatus() != 11 && pzcActivityGroupApply.getApplyStatus() != 12) { return R.fail("当前状态为【" + wxUtils.applyStatus(pzcActivityGroupApply.getApplyStatus()) + "】,不能进行此操作"); } //这里取消redis缓存 Long userId = LoginHelper.getUserId(); String result = stringRedisTemplate.opsForValue().get("officialMessage:" + userId); log.info("result:{}", result); if (result != null) { WxzApplyBo wxzApplyBo = JsonUtils.parseObject(result, WxzApplyBo.class); if (wxzApplyBo == null) { return R.fail("转换JSON异常"); } if (!wxzApplyBo.getApplyId().equals(applyId)) { return R.fail("申请方请求不存在"); } stringRedisTemplate.delete("officialMessage:" + userId); } else { return R.fail("申请方请求不存在"); } if(wxz==0) { return R.ok(); } pzcActivityGroupApply.setWxz(wxz); pzcActivityGroupApply.setApplyStatus(3); //直接是组队结束状态 pzcActivityGroupApplyMapper.updateById(pzcActivityGroupApply); //退还双方保证金 newSingleThreadExecutor.execute(() -> { // 更新 组队状态为已结束 PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApply.getGroupId()); PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId()); PzcUser groupUser = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); applyUser.setMoney(applyUser.getMoney().add(pzcActivityGroupApply.getMoney().subtract(new BigDecimal("10")))); groupUser.setMoney(groupUser.getMoney().add(pzcActivityGroup.getMoney().subtract(new BigDecimal("10")))); //退还发起人的保证金 //无限制确认到达加3分 applyUser.setIntegration(applyUser.getIntegration() + 3); applyUser.setIntegrationNow(applyUser.getIntegrationNow() + 3); groupUser.setIntegration(groupUser.getIntegration() + 3); groupUser.setIntegrationNow(groupUser.getIntegrationNow() + 3); pzcUserMapper.updateById(applyUser); pzcUserMapper.updateById(groupUser); //更新余额变动 wxUtils.insertUserHistory(applyUser.getUserId(), pzcActivityGroup.getActivityId(),2L, "组队结束,退还保证金 并收取活动费用 10派币", pzcActivityGroupApply.getMoney().subtract(new BigDecimal("10"))); wxUtils.insertUserHistory(groupUser.getUserId(), pzcActivityGroup.getActivityId(),2L, "组队结束,退还保证金 并收取活动费用 10派币", pzcActivityGroupApply.getMoney().subtract(new BigDecimal("10"))); pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); }); return R.ok(); } @GetMapping("/myHistory") //我的历史活动 public R> myHistory() { //我申请 并处于进行中的活动 Long userId = LoginHelper.getUserId(); List step1 = pzcActivityGroupApplyMapper.selectList( new QueryWrapper() .eq("user_id", userId).in("apply_status", 3,13,14,15)); step1.forEach( p -> { PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId()); PzcUser my = pzcUserMapper.selectById(p.getUserId()); PzcUser other = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); p.setOtherMoney(pzcActivityGroup.getMoney()); p.setOtherName(other.getNickname()); p.setOtherAvatar(other.getAvatar()); p.setOtherUserId(String.valueOf(other.getUserId())); p.setOtherLevel(Math.toIntExact(other.getUserLevel())); p.setMyAvatar(my.getAvatar()); p.setTitle(pzcActivityGroup.getTitle()); } ); List result = new java.util.ArrayList<>(); //申请我的 并处于进行中的活动 //1 找出所有我创建的组 List pzcActivityGroups = pzcActivityGroupMapper.selectList(new QueryWrapper().eq("user_id", userId)); List groupIds = pzcActivityGroups.stream().map(PzcActivityGroup::getGroupId).collect(java.util.stream.Collectors.toList()); if (groupIds.size() != 0) { List step2 = pzcActivityGroupApplyMapper.selectList(new QueryWrapper<>(new PzcActivityGroupApply()).in("group_id", groupIds).in("apply_status", 3,14,13,15)); step2.forEach( p -> { PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId()); PzcUser other = pzcUserMapper.selectById(p.getUserId()); PzcUser my = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); p.setOtherMoney(pzcActivityGroup.getMoney()); p.setOtherName(other.getNickname()); p.setOtherAvatar(other.getAvatar()); p.setOtherUserId(String.valueOf(other.getUserId())); p.setOtherLevel(Math.toIntExact(other.getUserLevel())); p.setMyAvatar(my.getAvatar()); p.setTitle(pzcActivityGroup.getTitle()); } ); result.addAll(step2); } result.addAll(step1); //按照更新时间倒序排列 List collect = result.stream().sorted((o1, o2) -> o2.getUpdateTime().compareTo(o1.getUpdateTime())).collect(Collectors.toList()); return R.ok(collect); } /** * -1 已取消 * 0 位于申请列表中 * 1 申请通过待确认时 * 2 确认通过进行中 * 3 组队结束 * 9发起方已确认 * 10申请方已确认 * 11 发起方已打卡 * 12 申请方已打卡 * 13 发起方已评价 * 14 申请方已评价 * 15 双方已评价 * * @return */ @GetMapping("/myTrips") //我的行程 public R> myTrips() { //我申请 并处于进行中的活动 Long userId = LoginHelper.getUserId(); List step1 = pzcActivityGroupApplyMapper.selectList( new QueryWrapper() .eq("user_id", userId).in("apply_status", 1, 2, 3, 9, 10, 11, 12, 14)); //, 14 我评价过了就从行程中移除 step1.forEach( p -> { PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId()); PzcUser my = pzcUserMapper.selectById(p.getUserId()); PzcUser other = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); p.setOtherMoney(pzcActivityGroup.getMoney()); p.setOtherName(other.getNickname()); p.setOtherAvatar(other.getAvatar()); p.setOtherUserId(String.valueOf(other.getUserId())); p.setOtherLevel(Math.toIntExact(other.getUserLevel())); p.setMyAvatar(my.getAvatar()); p.setTitle(pzcActivityGroup.getTitle()); } ); List result = new java.util.ArrayList<>(); //申请我的 并处于进行中的活动 //1 找出所有我创建的组 List pzcActivityGroups = pzcActivityGroupMapper.selectList(new QueryWrapper().eq("user_id", userId)); List groupIds = pzcActivityGroups.stream().map(PzcActivityGroup::getGroupId).collect(java.util.stream.Collectors.toList()); if (groupIds.size() != 0) { List step2 = pzcActivityGroupApplyMapper.selectList(new QueryWrapper<>(new PzcActivityGroupApply()).in("group_id", groupIds).in("apply_status", 1, 2, 3, 9, 10, 11, 12, 13));//13, 评价了就移除 step2.forEach( p -> { PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(p.getGroupId()); PzcUser other = pzcUserMapper.selectById(p.getUserId()); PzcUser my = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); p.setOtherMoney(pzcActivityGroup.getMoney()); p.setOtherName(other.getNickname()); p.setOtherAvatar(other.getAvatar()); p.setOtherUserId(String.valueOf(other.getUserId())); p.setOtherLevel(Math.toIntExact(other.getUserLevel())); p.setMyAvatar(my.getAvatar()); p.setTitle(pzcActivityGroup.getTitle()); } ); result.addAll(step2); } result.addAll(step1); //按照更新时间倒序排列 List collect = result.stream().sorted((o1, o2) -> o2.getUpdateTime().compareTo(o1.getUpdateTime())).collect(Collectors.toList()); return R.ok(collect); } /** * 查询活动组队申请列表列表 */ @GetMapping("/list") public TableDataInfo list(PzcActivityGroupApplyBo bo, PageQuery pageQuery) { bo.setUserId(LoginHelper.getUserId()); return iPzcActivityGroupApplyService.queryPageList(bo, pageQuery); } /** * 导出活动组队申请列表列表 */ @Log(title = "活动组队申请列表", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcActivityGroupApplyBo bo, HttpServletResponse response) { List list = iPzcActivityGroupApplyService.queryList(bo); ExcelUtil.exportExcel(list, "活动组队申请列表", PzcActivityGroupApplyVo.class, response); } /** * 获取活动组队申请列表详细信息 * * @param applyId 主键 */ @GetMapping("/{applyId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long applyId) { return R.ok(iPzcActivityGroupApplyService.queryById(applyId)); } /** * 申请参与组队 *

* 1 做校验 * 1.1 活动是否存在 1.2 活动是否已经开始 1.3 活动是否已经结束 1.4 活动是否已经满员 * 2 组是否还存在 *

*

* ==================== * 用户申请活动的时候 判断 是否足够缴纳保证金 */ @Log(title = "活动组队申请列表", businessType = BusinessType.INSERT) // @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcActivityGroupApplyBo bo) { log.info("申请参与组队:{}", JsonUtils.toJsonString(bo)); if(bo.getMoney().compareTo(new BigDecimal(100))<0) { return R.fail("申请失败,最低保障金为99派币~"); } if (!activityUtils.allCheck(Math.toIntExact(bo.getActivityId()), bo.getGroupId())) { return R.fail("申请失败,活动不存在或者已经结束或者组不存在"); } bo.setUserId(LoginHelper.getUserId()); if (iPzcActivityGroupApplyService.queryByUserIdAndGroupId(bo.getUserId(), bo.getGroupId()) != null) { return R.fail("申请失败,您已经申请过了"); } //====================================================== PzcUser applyUser = pzcUserMapper.selectById(bo.getUserId()); //我有2个币 申请需要 两个币 我则需要 根据当前他的余额来判断 log.info("申请参与组队 我目前的余额是: {} 申请需要的余额是:{}", applyUser.getMoney(), bo.getMoney()); if (applyUser.getMoney().compareTo(bo.getMoney()) < 0 || applyUser.getMoney().compareTo(new BigDecimal(100)) < 0) //100块钱 也没有就需要充值了 { return R.fail("申请失败,最低保障金为99派币~"); } return toAjax(iPzcActivityGroupApplyService.insertByBo(bo)); } /** * 修改活动组队申请列表 */ @Log(title = "活动组队申请列表", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcActivityGroupApplyBo bo) { bo.setUserId(LoginHelper.getUserId()); if (!activityUtils.allCheck(Math.toIntExact(bo.getActivityId()), bo.getGroupId())) { return R.fail("修改失败,活动不存在或者已经结束或者组不存在"); } if (!iPzcActivityGroupApplyService.queryByUserIdAndActivityId(bo.getUserId(), bo.getActivityId())) { return R.fail("修改失败,您还没有申请过该活动组"); } return toAjax(iPzcActivityGroupApplyService.updateByBo(bo)); } /** * 取消活动组队申请列表 * * @param applyIds 主键串 */ @Log(title = "活动组队申请列表", businessType = BusinessType.DELETE) @DeleteMapping("/{applyIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] applyIds) { return toAjax(iPzcActivityGroupApplyService.deleteWithValidByIds(Arrays.asList(applyIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcActivityGroupController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.map.MapBuilder; import cn.hutool.json.JSONUtil; import com.alibaba.excel.util.DateUtils; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.*; import top.flya.system.domain.bo.PzcActivityGroupBo; import top.flya.system.domain.bo.RefurbishBo; import top.flya.system.domain.vo.PzcActivityGroupApplyVo; import top.flya.system.domain.vo.PzcActivityGroupVo; import top.flya.system.domain.vo.RefurbishVO; import top.flya.system.mapper.*; import top.flya.system.service.IPzcActivityGroupApplyService; import top.flya.system.service.IPzcActivityGroupService; import top.flya.system.utils.WxUtils; import top.flya.system.utils.gaode.GaoDeMapUtil; import top.flya.system.xxlJob.ScheduledExecutorUtils; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static top.flya.system.config.ClientCache.concurrentHashMap; /** * 活动组队 * * @author ruoyi * @date 2023-07-10 */ @Validated @RequiredArgsConstructor @RestController @Slf4j @RequestMapping("/system/activityGroup") public class PzcActivityGroupController extends BaseController { private final IPzcActivityGroupService iPzcActivityGroupService; private final IPzcActivityGroupApplyService iPzcActivityGroupApplyService; private final PzcUserMapper pzcUserMapper; private final PzcActivityMapper pzcActivityMapper; private final PzcUserPhotoMapper pzcUserPhotoMapper; private final WxUtils wxUtils; private final PzcActivityGroupMapper pzcActivityGroupMapper; private final PzcActivityGroupApplyMapper pzcActivityGroupApplyMapper; private final PzcOfficialMapper pzcOfficialMapper; private final PzcUserTalkMapper pzcUserTalkMapper; private final GaoDeMapUtil gaoDeMapUtil; private final ExecutorService newSingleThreadExecutor = new ThreadPoolExecutor(10, 20, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() //指定拒绝策略 防止任务不执行 ); @PostMapping("/cancelIssueGroup") //取消组队的发布 public R cancelIssueGroup(@RequestParam("groupId") Long groupId) { log.info("取消组队的发布: {}", groupId); Long userId = LoginHelper.getUserId(); //首先看看组队是否是我发布的 并且是否有人申请 如果有人申请则不能取消 PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(groupId); if (pzcActivityGroup == null || !pzcActivityGroup.getUserId().equals(userId)) { return R.fail("组队不存在~"); } // 查询申请列表 如果有人申请则不能取消 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("group_id", groupId); List pzcActivityGroupApplies = pzcActivityGroupApplyMapper.selectList(queryWrapper); List delApplyIds = new ArrayList<>(); pzcActivityGroupApplies.forEach( pzcActivityGroupApply -> { if (pzcActivityGroupApply.getApplyStatus() != -1 && pzcActivityGroupApply.getApplyStatus() != 0 && pzcActivityGroupApply.getApplyStatus() != 15) { throw new RuntimeException("有组队正在进行中,不能取消"); } if (pzcActivityGroupApply.getApplyStatus() == 0) { delApplyIds.add(pzcActivityGroupApply.getActivityId()); //异步给对方发消息 通知对方已拒绝 修改为异步请求 newSingleThreadExecutor.execute(() -> { wxUtils.insertPzcOfficialMsg(userId, pzcActivityGroupApply.getUserId(), "来自您的申请组队【" + pzcActivityGroup.getTitle() + "】信息:", "您的申请组队【" + pzcActivityGroup.getTitle() + "】已被发布者取消", pzcActivityGroup.getGroupId(), pzcActivityGroup.getActivityId()); }); } } ); //删除申请 if (delApplyIds.size() > 0) { pzcActivityGroupApplyMapper.deleteBatchIds(delApplyIds); } //删除组队 pzcActivityGroupMapper.deleteById(groupId); return R.ok(); } @PostMapping("/refurbish") //刷新 public R refurbish(@RequestBody RefurbishBo refurbishBo) throws Exception { log.info("刷新: {}", JsonUtils.toJsonString(refurbishBo)); //首先查询这个组队是否是我发起的 PzcActivityGroupApply pzcActivityGroupApply = pzcActivityGroupApplyMapper.selectById(refurbishBo.getApplyId()); if (pzcActivityGroupApply == null) { return R.fail("申请不存在"); } PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApply.getGroupId()); Pattern pattern = Pattern.compile("【(.*?)】(.*?)"); Matcher matcher = pattern.matcher(pzcActivityGroup.getAddress()); String end = ""; if (matcher.find()) { end = matcher.group(1); } else { return R.fail("地址解析失败,请联系管理员"); } String[] startJwd = refurbishBo.getAddress().split(","); Long data = gaoDeMapUtil.getDistance(refurbishBo.getAddress(), end).getData(); refurbishBo.setAddress(gaoDeMapUtil.getAddress(startJwd[0], startJwd[1]).getData().toString()); log.info("调用高德API获取到的用户目前地址为: {}", refurbishBo.getAddress()); log.info("调用高德API获取到的用户距离目标地址距离为: {}", data); if (refurbishBo.getRole() == 0) //申请方 { //判断申请方现在是否可以打卡 pzcActivityGroupApply.setApplyAddress(refurbishBo.getAddress()); pzcActivityGroupApplyMapper.updateById(pzcActivityGroupApply); RefurbishVO refurbishVO = new RefurbishVO(); refurbishVO.setApplyAddress(refurbishBo.getAddress()); refurbishVO.setApplyId(refurbishBo.getApplyId()); refurbishVO.setStartAddress(pzcActivityGroupApply.getStartAddress()); refurbishVO.setDistance(data); log.info("刷新返回的信息为: {}", JSONUtil.toJsonPrettyStr(refurbishVO)); return R.ok(refurbishVO); } else { pzcActivityGroupApply.setStartAddress(refurbishBo.getAddress()); pzcActivityGroupApplyMapper.updateById(pzcActivityGroupApply); RefurbishVO refurbishVO = new RefurbishVO(); refurbishVO.setApplyAddress(pzcActivityGroupApply.getApplyAddress()); refurbishVO.setApplyId(refurbishBo.getApplyId()); refurbishVO.setStartAddress(refurbishBo.getAddress()); refurbishVO.setDistance(data); log.info("刷新返回的信息为: {}", JSONUtil.toJsonPrettyStr(refurbishVO)); return R.ok(refurbishVO); } } @PostMapping("/cancel") //取消 双方都可以取消 @Transactional @RepeatSubmit() public R cancel(@RequestParam("applyId") Integer applyId) { //首先查询这个组队是否是我发起的 PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId.longValue()); if (pzcActivityGroupApplyVo == null) { return R.fail("申请不存在"); } Long userId = LoginHelper.getUserId(); Long groupId = pzcActivityGroupApplyVo.getGroupId(); PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId); if (pzcActivityGroupVo == null) { return R.fail("组队不存在"); } PzcUser otherUser = null; if (pzcActivityGroupVo.getUserId().equals(userId)) //我是发起人 取消 { otherUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); } else { //我是申请人 otherUser = pzcUserMapper.selectById(pzcActivityGroupMapper.selectById(groupId).getUserId()); } // 给对方发官方消息 通知对方已取消 wxUtils.insertPzcOfficialMsg(userId, otherUser.getUserId(), "来自" + otherUser.getNickname() + "与您的组队信息:", "在【" + pzcActivityGroupVo.getTitle() + "】组队中途,对方已经取消本次组队。您可以再次同意或者申请其他用户", groupId, pzcActivityGroupVo.getActivityId()); Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus(); if (applyStatus != 0 && applyStatus != 1 && applyStatus != 9 && applyStatus != 10) { return R.fail("该订单位于【" + wxUtils.applyStatus(applyStatus) + "】状态,不可取消"); } //查询用户是否有免责取消次数 PzcUser pzcUser = pzcUserMapper.selectById(LoginHelper.getUserId()); if (pzcUser.getExemptCancel() > 0) { pzcUser.setExemptCancel(pzcUser.getExemptCancel() - 1); } else { pzcUser.setMoney(pzcUser.getMoney().subtract(new BigDecimal("10"))); //扣除10派币 wxUtils.insertUserHistory(pzcUser.getUserId(), pzcActivityGroupVo.getActivityId(), 4L, "取消组队扣除10派币", new BigDecimal("10").negate()); } pzcUserMapper.updateById(pzcUser); //修改状态为 已取消 newSingleThreadExecutor.execute(() -> { // 更新 组队状态为已结束 PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId()); pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); }); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), -1)); } @PostMapping("/cancelByGroupIn") //活动过程中取消组队 扣除保证金 + 20派币 退还对方派币 + 对方的保证金 通知对方 @Transactional @RepeatSubmit() public R cancelByGroupIn(@RequestParam("applyId") Integer applyId) { Long userId = LoginHelper.getUserId(); PzcUser my = pzcUserMapper.selectById(userId); PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId.longValue()); if (pzcActivityGroupApplyVo == null) { return R.fail("申请不存在"); } PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApplyVo.getGroupId()); if (pzcActivityGroupApplyVo.getUserId().equals(userId)) //我是申请方 { //把钱都返还给发起方 BigDecimal money = pzcActivityGroupApplyVo.getMoney(); money = money.subtract(new BigDecimal("20")); PzcUser startUser = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); startUser.setMoney(startUser.getMoney().add(money).add(pzcActivityGroup.getMoney())); //全额返回给发起方的保证金 + 对方扣除 0.2保证金 后的派币 pzcUserMapper.updateById(startUser); BigDecimal finalMoney = money; newSingleThreadExecutor.execute(() -> { //官方推送消息 wxUtils.insertPzcOfficialMsg(userId, startUser.getUserId(), "来自" + my.getNickname() + "与您的组队信息:", "很遗憾地通知您:您在【" + pzcActivityGroup.getTitle() + "】组队活动中,申请方已经取消本次组队活动。对方的违约金 【" + finalMoney + "派币】已纳入您的账户。您可以再次同意或者申请其他用户。", pzcActivityGroup.getGroupId(), pzcActivityGroup.getActivityId()); //历史记录 wxUtils.insertUserHistory(userId, pzcActivityGroup.getActivityId(), 4L, "在【" + pzcActivityGroup.getTitle() + "】活动中途取消组队,扣除违约金 【" + pzcActivityGroup.getMoney() + "】派币", pzcActivityGroup.getMoney().negate()); wxUtils.insertUserHistory(startUser.getUserId(), pzcActivityGroup.getActivityId(), 2L, "在【" + pzcActivityGroup.getTitle() + "】活动中途取消组队,违约金所得 【" + pzcActivityGroup.getMoney().subtract(new BigDecimal("20")) + "】派币", pzcActivityGroup.getMoney().subtract(new BigDecimal("20"))); }); } else { //我是发起方 PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); applyUser.setMoney(applyUser.getMoney(). add(pzcActivityGroup.getMoney().subtract(new BigDecimal("20"))) .add(pzcActivityGroupApplyVo.getMoney())); //全额返回给申请方的保证金 pzcUserMapper.updateById(applyUser); newSingleThreadExecutor.execute(() -> { //官方推送消息 wxUtils.insertPzcOfficialMsg(userId, applyUser.getUserId(), "来自" + my.getNickname() + "与您的组队信息:", "很遗憾地通知您:您在在【" + pzcActivityGroup.getTitle() + "】组队活动中,发起方已经取消本次组队活动。对方的违约金 【" + pzcActivityGroup.getMoney().subtract(new BigDecimal("20")) + "派币】已纳入您的账户。您可以再次同意或者申请其他用户。", pzcActivityGroup.getGroupId(), pzcActivityGroup.getActivityId()); //历史记录 wxUtils.insertUserHistory(userId, pzcActivityGroup.getActivityId(), 4L, "在【" + pzcActivityGroup.getTitle() + "】活动中途取消组队,违约金 【" + pzcActivityGroup.getMoney() + "】派币", pzcActivityGroup.getMoney().negate()); wxUtils.insertUserHistory(applyUser.getUserId(), pzcActivityGroup.getActivityId(), 2L, "在【" + pzcActivityGroup.getTitle() + "】活动中途取消组队,违约金所得 【" + pzcActivityGroup.getMoney().subtract(new BigDecimal("20")) + "】派币", pzcActivityGroup.getMoney().subtract(new BigDecimal("20"))); }); } newSingleThreadExecutor.execute(() -> { // 更新 组队状态为已结束 pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); }); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), -1));//修改状态为 已取消 } /** * 我创建的活动的申请列表 * 思路整理 * 首先查出所有 GroupId * 然后查出groupId 对应的申请列表 * * @return */ @GetMapping("/applyList") public R> applyList() { PzcActivityGroupBo bo = new PzcActivityGroupBo(); bo.setUserId(LoginHelper.getUserId()); List pzcActivityGroupVos = iPzcActivityGroupService.queryList(bo); List groupIds = pzcActivityGroupVos.stream().map(PzcActivityGroupVo::getGroupId).collect(Collectors.toList()); if (groupIds.size() == 0) { return R.ok(); } List pzcActivityGroupApplyVos = iPzcActivityGroupApplyService.queryListByGroupIds(groupIds); pzcActivityGroupApplyVos.forEach( s -> { PzcUser pzcUser = pzcUserMapper.selectById(s.getUserId()); s.setNickName(pzcUser.getNickname()); s.setAvatar(pzcUser.getAvatar()); Integer region = pzcActivityGroupVos.stream().filter(s1 -> s1.getGroupId().equals(s.getGroupId())).findFirst().get().getRegion(); String title = ""; PzcActivity pzcActivity = pzcActivityMapper.selectById(s.getActivityId()); if (s.getActivityId() == 0) { PzcRegion pzcRegion = pzcRegionMapper.selectById(region); if (pzcRegion != null) { title = "【" + pzcRegion.getName() + "】"; } log.info("申请活动列表 title:{}", title); } else { if (pzcActivity != null) { title = pzcActivity.getTitle(); } else { title = "【活动已结束】"; } } s.setActivityTitle(title); s.setGroupTitle(pzcActivityGroupVos.stream().filter(s1 -> s1.getGroupId().equals(s.getGroupId())).findFirst().get().getTitle()); } ); List result = pzcActivityGroupApplyVos.stream().filter(s -> s.getApplyStatus() == 0).collect(Collectors.toList());//过滤掉已取消的 return R.ok(result); } @GetMapping("/userInfo") //查看申请人或者发起人信息 public R userInfo(@RequestParam("userId") Long userId, @RequestParam("groupId") Long groupId) { //首先查询该用户是否申请了我的组 申请了 才有资格去查看 这个不应该是申请的 人才能看 双方都能互相看 PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryByUserIdAndGroupId(userId, groupId); if (pzcActivityGroupApplyVo == null) { PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId); if (!userId.equals(pzcActivityGroupVo.getUserId())) { return R.fail("无权查看该用户信息"); } pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryByUserIdAndGroupId(LoginHelper.getUserId(), groupId); } PzcUser pzcUser = pzcUserMapper.selectById(userId); pzcUser.setMoney(null); pzcUser.setUserPhoto(pzcUserPhotoMapper.selectList(new QueryWrapper<>(new PzcUserPhoto()).eq("user_id", userId))); PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(groupId); pzcActivityGroup.setAddress(pzcActivityGroup.getAddress().substring(pzcActivityGroup.getAddress().indexOf("】") + 1)); pzcActivityGroupApplyVo.setActivityTitle(pzcActivityGroup.getActivityName()); pzcUser.setPzcActivityGroupApplyVo(pzcActivityGroupApplyVo); pzcUser.setPzcActivityGroup(pzcActivityGroup); pzcUser.setLiveStatus(concurrentHashMap.get(userId) != null); pzcUser.setNotReadCount(pzcUserTalkMapper.selectNotReadCount(userId, LoginHelper.getUserId(), LoginHelper.getUserId())); pzcUser.setExemptCancel(pzcUserMapper.selectById(LoginHelper.getUserId()).getExemptCancel()); //获取我的免责取消次数 return R.ok(pzcUser); } /** * 13 发起方已评价 * 14 申请方已评价 * 15 双方已评价 * * @param applyId * @return */ @PostMapping("/pj") //双方评价 (可选) @Transactional public R pj(@RequestParam("applyId") Integer applyId, @RequestParam("score") Integer score) { wxUtils.checkApplyScore(score); PzcActivityGroupApplyVo pzcActivityGroupApplyVo = wxUtils.checkApplyPj(applyId.longValue()); //首先获取我的UserId Long userId = LoginHelper.getUserId(); Long groupId = pzcActivityGroupApplyVo.getGroupId(); Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus(); if (pzcActivityGroupApplyVo.getUserId().equals(userId)) { //获取对方 userId 并且修改对方积分 Long otherUserId = pzcActivityGroupMapper.selectById(groupId).getUserId(); PzcUser otherUser = pzcUserMapper.selectById(otherUserId); if (applyStatus == 13) { return R.fail("您已经评价过了 不可重复操作"); } otherUser.setIntegration(otherUser.getIntegration() + score); otherUser.setIntegrationNow(otherUser.getIntegrationNow() + score); wxUtils.updateUserMsg(otherUser); pzcUserMapper.updateById(otherUser); if (applyStatus == 14) //发起方评价了 { return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 15)); //双方都已评价 } return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 13));//申请方评价 } //判断当前 用户是否为组队发起人 如果不是 直接报错 PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId); if (pzcActivityGroupVo == null) { return R.fail("组队不存在"); } if (!pzcActivityGroupVo.getUserId().equals(userId)) { return R.fail("你不是组队发起人"); } if (applyStatus == 14) { return R.fail("您已经评价过了 不可重复操作"); } // ============================================================================================ Long otherUserId = pzcActivityGroupApplyVo.getUserId(); PzcUser otherUser = pzcUserMapper.selectById(otherUserId); otherUser.setIntegration(otherUser.getIntegration() + score); otherUser.setIntegrationNow(otherUser.getIntegrationNow() + score); wxUtils.updateUserMsg(otherUser); pzcUserMapper.updateById(otherUser); //看看申请方是否评价了 if (applyStatus == 13) //申请方评价了 { return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 15)); //双方都已评价 } return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 14));//发起方评价 } /** * 双方都到达了目的地 开始扣手续费 * * @param applyId * @return */ @PostMapping("/confirmReach") //确认到达目的地 @Transactional public R confirmReach(@RequestParam("applyId") Integer applyId) { PzcActivityGroupApplyVo pzcActivityGroupApplyVo = wxUtils.checkApplyConfirm(applyId.longValue()); Long userId = LoginHelper.getUserId(); PzcUser my = pzcUserMapper.selectById(userId); Long groupId = pzcActivityGroupApplyVo.getGroupId(); Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus(); if (applyStatus != 2 && applyStatus != 11 && applyStatus != 12) { return R.fail("该订单目前状态为【" + wxUtils.applyStatus(applyStatus) + "】不可确认"); } //获取发起方的保证金 PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId); if (pzcActivityGroupVo == null) { return R.fail("组队不存在"); } PzcActivity pzcActivity = pzcActivityMapper.selectById(pzcActivityGroupVo.getActivityId()); if (pzcActivityGroupApplyVo.getUserId().equals(userId)) { //我是申请方 if (applyStatus == 11) //发起方确认了 { BigDecimal money = pzcActivityGroupVo.getMoney(); money = money.subtract(new BigDecimal("10")); //将保证金还给发起方 PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId()); pzcUser.setMoney(pzcUser.getMoney().add(money)); pzcUserMapper.updateById(pzcUser); //======================================================================== //获取申请方的保证金 PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); BigDecimal applyMoney = pzcActivityGroupApplyVo.getMoney(); applyMoney = applyMoney.subtract(new BigDecimal("10")); //将保证金还给申请方 applyUser.setMoney(applyUser.getMoney().add(applyMoney)); pzcUserMapper.updateById(applyUser); BigDecimal finalMoney = money; BigDecimal finalApplyMoney = applyMoney; newSingleThreadExecutor.execute(() -> { wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, "组队完成退还保证金 【" + finalMoney + "】 派币", finalMoney); wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, "组队完成退还保证金 【" + finalApplyMoney + "】 派币", finalMoney); wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null); wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null); PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApplyVo.getGroupId()); // 更新 组队状态为已结束 pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); //更新双方积分 PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); PzcUser pzcUser2 = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); pzcUser1.setIntegration(pzcUser1.getIntegration() + 3); pzcUser1.setIntegrationNow(pzcUser1.getIntegrationNow() + 3); pzcUser2.setIntegration(pzcUser2.getIntegration() + 3); pzcUser2.setIntegrationNow(pzcUser2.getIntegrationNow() + 3); wxUtils.updateUserMsg(pzcUser1); wxUtils.updateUserMsg(pzcUser2); }); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 3)); //双方都已确认 } if (applyStatus == 12) { return R.fail("您已经确认过了 不可重复操作"); } //申请方确认了 给发起方推送微信消息 PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId()); Map dataMap = new HashMap<>(); dataMap.put("thing4", MapBuilder.create().put("value", my.getNickname()).build()); dataMap.put("thing5", MapBuilder.create().put("value", "对方已到达,您需在活动时间内到达").build()); dataMap.put("time6", MapBuilder.create().put("value", DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")).build()); log.info("发起方确认到达目的地,给申请方推送微信消息:{}", JsonUtils.toJsonString(dataMap)); newSingleThreadExecutor.execute(() -> { wxUtils.sendArriveMsg(pzcUser.getOpenid(), dataMap); }); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 12));//申请方确认 } //判断当前 用户是否为组队发起人 如果不是 直接报错 if (!pzcActivityGroupVo.getUserId().equals(userId)) { return R.fail("你不是组队发起人"); } //看看申请方是否确认了 if (applyStatus == 12) //我是发起方 { //获取发起方的保证金 BigDecimal money = pzcActivityGroupVo.getMoney(); money = money.subtract(new BigDecimal("10")); //将保证金还给发起方 PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId()); pzcUser.setMoney(pzcUser.getMoney().add(money)); pzcUserMapper.updateById(pzcUser); //======================================================================== //获取申请方的保证金 PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); BigDecimal applyMoney = pzcActivityGroupApplyVo.getMoney(); applyMoney = applyMoney.subtract(new BigDecimal("10")); //将保证金还给申请方 applyUser.setMoney(applyUser.getMoney().add(applyMoney)); pzcUserMapper.updateById(applyUser); BigDecimal finalMoney = money; BigDecimal finalApplyMoney = applyMoney; newSingleThreadExecutor.execute(() -> { wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, "组队完成退还保证金 " + finalMoney + " 派币", finalMoney); wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 2L, "组队完成退还保证金 " + finalApplyMoney + " 派币", finalMoney); wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null); wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupVo.getActivityId(), 1L, JsonUtils.toJsonString(pzcActivity), null); PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupApplyVo.getGroupId()); // 更新 组队状态为已结束 pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); //更新双方积分 PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroup.getUserId()); PzcUser pzcUser2 = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); pzcUser1.setIntegration(pzcUser1.getIntegration() + 3); pzcUser1.setIntegrationNow(pzcUser1.getIntegrationNow() + 3); pzcUser2.setIntegration(pzcUser2.getIntegration() + 3); pzcUser2.setIntegrationNow(pzcUser2.getIntegrationNow() + 3); wxUtils.updateUserMsg(pzcUser1); wxUtils.updateUserMsg(pzcUser2); }); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 3)); //双方都已确认 } if (applyStatus == 11) { return R.fail("您已经确认过了 不可重复操作"); } //发起方确认了 给申请方推送微信消息 PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); Map> dataMap = new HashMap<>(); dataMap.put("thing4", MapBuilder.create().put("value", my.getNickname()).build()); dataMap.put("thing5", MapBuilder.create().put("value", "对方已到达,您需在活动时间内到达").build()); //对方已到达约定的见面地点,您需在【"+pzcActivityGroupVo.getActivityTime()+"】前到达目的地,否则,按照违约处理。或您也可以与对方沟通,申请无限制确认到达,以保证组队的正常。 dataMap.put("time6", MapBuilder.create().put("value", DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")).build()); log.info("申请方确认到达目的地,给发起方推送微信消息:{}", JsonUtils.toJsonString(dataMap)); newSingleThreadExecutor.execute(() -> { wxUtils.sendArriveMsg(pzcUser.getOpenid(), dataMap); }); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 11));//发起方确认 } @GetMapping("/applyRole") public R applyRole(@RequestParam("applyId") Integer applyId) { Long userId = LoginHelper.getUserId(); PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId.longValue()); if (pzcActivityGroupApplyVo.getUserId().equals(userId)) { return R.ok(0); //我是申请方 } else { return R.ok(1); //我是发起方 } } @PostMapping("/confirm") //确认申请 (这里判断 保证金 是否足够缴纳 保证 新人卡 bug) @Transactional public R confirm(@RequestParam("applyId") Integer applyId) { PzcActivityGroupApplyVo pzcActivityGroupApplyVo = wxUtils.checkApplyConfirm(applyId.longValue()); Long userId = LoginHelper.getUserId(); Long groupId = pzcActivityGroupApplyVo.getGroupId(); Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus(); if (applyStatus == 2) { return R.fail("该订单进行中,不可确认"); } PzcActivityGroupVo group = iPzcActivityGroupService.queryById(groupId); //获取双方保证金 BigDecimal applyMoney = pzcActivityGroupApplyVo.getMoney(); BigDecimal startMoney = group.getMoney(); //获取双方 PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApplyVo.getUserId()); PzcUser startUser = pzcUserMapper.selectById(group.getUserId()); //如果可以确认 判断 是那一方确认的 if (pzcActivityGroupApplyVo.getUserId().equals(userId)) {// 自己是申请方 申请方还得等发起方最后确认时间地点 if (applyStatus == 10) { return R.fail("您已经确认过了 不可重复操作"); } //如果是自己确认的 则修改状态为 已确认 if (applyStatus == 9) //发起方确认了 { log.info("申请方的余额与保证金 {}----{}", applyUser.getMoney(), applyMoney); // 如果有一方保证金不足以缴纳 则报错 if (applyUser.getMoney().compareTo(applyMoney) < 0) { return R.fail("您的保证金不足以缴纳"); } //双方都已确认 开始 扣除保证金 //获取申请方保证金 applyUser.setMoney(applyUser.getMoney().subtract(applyMoney)); pzcUserMapper.updateById(applyUser); //获取发起方保证金 startUser.setMoney(startUser.getMoney().subtract(startMoney)); pzcUserMapper.updateById(startUser); //存入历史记录 wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, "组队开始扣除保证金 【" + applyMoney + "】 派币", applyMoney.negate()); wxUtils.insertUserHistory(group.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, "组队开始扣除保证金 【" + startMoney + "】 派币", startMoney.negate()); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 2)); //双方都已确认 } else { return R.fail("发起方还未确认最后时间地点,请继续保持沟通哦"); } } else { //自己是发起方 //判断当前 用户是否为组队发起人 如果不是 直接报错‘ PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId); if (pzcActivityGroupVo == null) { return R.fail("组队不存在"); } if (!pzcActivityGroupVo.getUserId().equals(userId)) { return R.fail("你不是组队发起人"); } if (applyStatus == 9) { return R.fail("您已经确认过了 不可重复操作"); } //看看申请方是否确认了 if (applyStatus == 10) //申请方确认了 { log.info("发起方的余额与保证金 {}----{}", startUser.getMoney(), startMoney); if (startUser.getMoney().compareTo(startMoney) < 0) { return R.fail("您的的保证金不足以缴纳"); } //双方都已确认 开始 扣除保证金 //获取申请方保证金 applyUser.setMoney(applyUser.getMoney().subtract(applyMoney)); pzcUserMapper.updateById(applyUser); //获取发起方保证金 startUser.setMoney(startUser.getMoney().subtract(startMoney)); pzcUserMapper.updateById(startUser); newSingleThreadExecutor.execute(() -> { //存入历史记录 wxUtils.insertUserHistory(pzcActivityGroupApplyVo.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, "组队开始扣除保证金 【" + applyMoney + "】 派币", applyMoney.negate()); wxUtils.insertUserHistory(group.getUserId(), pzcActivityGroupApplyVo.getActivityId(), 3L, "组队开始扣除保证金 【" + startMoney + "】 派币", startMoney.negate()); }); return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 2)); //双方都已确认 } return R.ok(iPzcActivityGroupApplyService.updateStatus(applyId.longValue(), 9));//发起方确认 } } /** * 同意用户申请 进入下一阶段 * 同意用户申请时 先判断对方是否 处于组队进程 *

*

* -1 已取消 0 位于申请列表中 1 申请通过待确认时 * 2 确认通过进行中 3 组队结束 9发起方已确认 * 10申请方已确认 11 发起方已打卡 12 申请方已打卡 * 13 申请方已评价 14 发起方已评价 15 双方已评价 * * @return */ @PostMapping("/apply") public R apply(@RequestParam("applyId") Long applyId) { //首先查询这个组队是否是我发起的 PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId); if (pzcActivityGroupApplyVo == null) { return R.fail("申请不存在"); } Long userId = LoginHelper.getUserId(); PzcUser my = pzcUserMapper.selectById(userId); Long groupId = pzcActivityGroupApplyVo.getGroupId(); PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId); if (pzcActivityGroupVo == null) { return R.fail("组队不存在"); } if (!pzcActivityGroupVo.getUserId().equals(userId)) { return R.fail("你不是组队发起人"); } //判断对方是否 处于 组队进程 如果是 则不可同意 Long applyUserId = pzcActivityGroupApplyVo.getUserId(); //获取活动Id Long activityId = pzcActivityGroupVo.getActivityId(); //获取活动组队列表 除了自己这个队伍外 List groups = pzcActivityGroupMapper.selectList(new QueryWrapper().eq("activity_id", activityId)); List groupIds = groups.stream().filter(s -> !s.getGroupId().equals(groupId)).map(PzcActivityGroup::getGroupId).collect(Collectors.toList()); if (groupIds.size() != 0) { //然后获取当前对方申请了几个队伍 判断每个队伍的进程 如果有 进程处于 组队状态中 则不可以同意 List applies = pzcActivityGroupApplyMapper.selectList(new QueryWrapper().in("group_id", groupIds).eq("user_id", applyUserId)); applies.forEach( a -> { if (a.getApplyStatus() != 3 && a.getApplyStatus() != 13 && a.getApplyStatus() != 14 && a.getApplyStatus() != 15 && a.getApplyStatus() != -1) { log.info("对方当前进程为 {} 详细信息为 {}", a.getApplyStatus(), JsonUtils.toJsonString(a)); throw new RuntimeException("该用户已经处于组队进程中 等待对方结束活动再试哦"); } } ); } //判断一下 我是否在当前活动下 有未完成的组队 如果有就不能 同意其他的申请 List applies = pzcActivityGroupApplyMapper.selectList(new QueryWrapper().eq("group_id", groupId). eq("activity_id", activityId).notIn("apply_status",-1,0,3,13,14,15)); log.info("当前申请人的申请列表为:{}",JSONUtil.toJsonPrettyStr(applies)); if(applies.size()>1) { return R.fail("您当前有未完成的组队,无法同意其他申请"); } //修改状态为 已同意 Integer integer = iPzcActivityGroupApplyService.updateStatus(applyId, 1); if (integer == null || integer != 1) { return R.fail("修改状态失败"); } //给对方发消息 已经同意了对方的申请 请尽快确认 PzcUser otherUser = pzcUserMapper.selectById(applyUserId); wxUtils.insertPzcOfficialMsg(my.getUserId(), otherUser.getUserId(),"来自" + my.getNickname() + "与您的组队信息:","您的组队申请已经被对方同意,请尽快确认~",groupId,activityId); try { // 创建一个任务逻辑,接受参数并在任务执行时使用 ScheduledExecutorUtils.RunnableWithParams task = (params) -> { System.out.println("Task executed at: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); if (params.length > 0) { System.out.println("Parameter received: " + params[0]); } // 在这里添加你的任务逻辑 PzcActivityGroupApply pzcActivityGroupApply = pzcActivityGroupApplyMapper.selectById((Serializable) params[1]); log.info("定时任务执行中,当前申请信息为:{}", JsonUtils.toJsonString(pzcActivityGroupApply)); if (pzcActivityGroupApply.getApplyStatus() == 1) { //如果对方还未确认 则修改状态为 已取消 iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1); //发起方扣除10派币或者免责取消的一次机会 PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId()); if (pzcUser.getExemptCancel() == 0) { pzcUser.setMoney(pzcUser.getMoney().subtract(new BigDecimal("10"))); pzcUserMapper.updateById(pzcUser); wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 3L, "组队取消扣除保证金 【10】 派币", new BigDecimal("10").negate()); } else { pzcUser.setExemptCancel(pzcUser.getExemptCancel() - 1); pzcUserMapper.updateById(pzcUser); } //修改组队状态为已完成 PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId()); pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); //给对方发消息 对方未确认 申请已取消 PzcOfficial pzcOfficial1 = new PzcOfficial(); pzcOfficial1.setIsRead(0L); PzcUser otherUser1 = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId()); pzcOfficial1.setToUserId(otherUser1.getUserId()); pzcOfficial1.setTitle("派之城官方提醒您:"); pzcOfficial1.setContent("您好,由于对方未确认地址,您的组队已取消。平台已对对方进行相应惩罚。"); pzcOfficial1.setGroupId(groupId); pzcOfficial1.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial1); PzcOfficial pzcOfficial2 = new PzcOfficial(); pzcOfficial2.setIsRead(0L); pzcOfficial2.setToUserId(pzcActivityGroupVo.getUserId()); pzcOfficial2.setTitle("派之城官方提醒您:"); pzcOfficial2.setContent("您好,由于您未进行确认地址,您的组队已取消,平台扣除您一次免责取消机会或10派币。(具体根据您免责机会次数确定)"); pzcOfficial2.setGroupId(groupId); pzcOfficial2.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial2); } if (pzcActivityGroupApply.getApplyStatus() == 9) { //发起方确认了地址 而申请方未确认 //如果对方还未确认 则修改状态为 已取消 iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1); //申请方扣除10派币或者免责取消的一次机会 PzcUser pzcUser = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId()); if (pzcUser.getExemptCancel() == 0) { pzcUser.setMoney(pzcUser.getMoney().subtract(new BigDecimal("10"))); pzcUserMapper.updateById(pzcUser); wxUtils.insertUserHistory(pzcActivityGroupApply.getUserId(), pzcActivityGroupApply.getActivityId(), 3L, "组队取消扣除保证金 【10】 派币", new BigDecimal("10").negate()); } else { pzcUser.setExemptCancel(pzcUser.getExemptCancel() - 1); pzcUserMapper.updateById(pzcUser); } //修改组队状态为已完成 PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId()); pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); //给对方发消息 对方未确认 申请已取消 PzcOfficial pzcOfficial1 = new PzcOfficial(); pzcOfficial1.setIsRead(0L); PzcUser otherUser1 = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId()); //申请方 pzcOfficial1.setToUserId(otherUser1.getUserId()); pzcOfficial1.setTitle("派之城官方提醒您:"); pzcOfficial1.setContent("您好,由于您未进行确认地址,您的组队已取消,平台扣除您一次免责取消机会或10派币。(具体根据您免责机会次数确定)"); pzcOfficial1.setGroupId(groupId); pzcOfficial1.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial1); PzcOfficial pzcOfficial2 = new PzcOfficial(); pzcOfficial2.setIsRead(0L); pzcOfficial2.setToUserId(pzcActivityGroupVo.getUserId()); //发起方 pzcOfficial2.setTitle("派之城官方提醒您:"); pzcOfficial2.setContent("您好,由于对方未确认地址,您的组队已取消。平台已对对方进行相应惩罚。"); pzcOfficial2.setGroupId(groupId); pzcOfficial2.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial2); } if (pzcActivityGroupApply.getApplyStatus() == 10) { //申请方确认了地址 //如果对方还未确认 则修改状态为 已取消 iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1); //修改组队状态为已完成 PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId()); pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); //双方均扣除10派币 PzcUser startUser = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId()); startUser.setMoney(startUser.getMoney().subtract(new BigDecimal("10"))); pzcUserMapper.updateById(startUser); wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 3L, "组队取消扣除保证金 【10】 派币", new BigDecimal("10").negate()); PzcUser applyUser = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId()); applyUser.setMoney(applyUser.getMoney().subtract(new BigDecimal("10"))); pzcUserMapper.updateById(applyUser); wxUtils.insertUserHistory(pzcActivityGroupApply.getUserId(), pzcActivityGroupApply.getActivityId(), 3L, "组队取消扣除保证金 【10】 派币", new BigDecimal("10").negate()); } if (pzcActivityGroupApply.getApplyStatus() == 11 || pzcActivityGroupApply.getApplyStatus() == 12) { //如果对方还未确认 则修改状态为 已取消 iPzcActivityGroupApplyService.updateStatus((Long) params[1], -1); //修改组队状态为已完成 PzcActivityGroup pzcActivityGroup = pzcActivityGroupMapper.selectById(pzcActivityGroupVo.getGroupId()); pzcActivityGroup.setStatus(1); //已结束 pzcActivityGroupMapper.updateById(pzcActivityGroup); if (pzcActivityGroupApply.getApplyStatus() == 11) //发起方打卡了 申请方 全责 { BigDecimal money = pzcActivityGroupApply.getMoney(); PzcOfficial pzcOfficial1 = new PzcOfficial(); pzcOfficial1.setIsRead(0L); pzcOfficial1.setFromUserId(null); pzcOfficial1.setToUserId(pzcActivityGroupApply.getUserId()); pzcOfficial1.setTitle("派之城提醒您:"); pzcOfficial1.setContent("您好,由于您在活动结束前未进行到约定地方打卡签到,平台已对您进行违约处理,已扣除您全部保障金。"); pzcOfficial1.setGroupId(groupId); pzcOfficial1.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial1); PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroupVo.getUserId()); pzcUser1.setMoney(pzcUser1.getMoney().add(pzcActivityGroup.getMoney()).add(money.subtract(new BigDecimal("20")))); pzcUserMapper.updateById(pzcUser1); //发起方获得申请方 扣除20派币 后的保证金 PzcOfficial pzcOfficial2 = new PzcOfficial(); pzcOfficial2.setIsRead(0L); pzcOfficial2.setFromUserId(null); pzcOfficial2.setToUserId(pzcActivityGroupVo.getUserId()); pzcOfficial2.setTitle("派之城提醒您:"); pzcOfficial2.setContent("您好,您在【" + pzcActivityGroupVo.getTitle() + "】组队活动中,由于对方未在活动结束前进行签到打卡,平台已对对方进行违约处理。对方的违约金【" + money.subtract(new BigDecimal("20")) + "】派币已纳入您的账户。您可以再次同意或申请其他用户。"); pzcOfficial2.setGroupId(groupId); pzcOfficial2.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial2); wxUtils.insertUserHistory(pzcActivityGroupApply.getUserId(), pzcActivityGroupApply.getActivityId(), 3L, "活动时间到 自动取消组队 扣除保证金 【" + money + "】 派币", money.negate()); } if (pzcActivityGroupApply.getApplyStatus() == 12) //申请方打卡了 发起方 全责 { BigDecimal money = pzcActivityGroupVo.getMoney(); PzcOfficial pzcOfficial1 = new PzcOfficial(); pzcOfficial1.setIsRead(0L); pzcOfficial1.setFromUserId(null); pzcOfficial1.setToUserId(pzcActivityGroupVo.getUserId()); pzcOfficial1.setTitle("派之城提醒您:"); pzcOfficial1.setContent("您好,由于您在活动结束前未进行到约定地方打卡签到,平台已对您进行违约处理,已扣除您全部保障金。"); pzcOfficial1.setGroupId(groupId); pzcOfficial1.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial1); PzcUser pzcUser1 = pzcUserMapper.selectById(pzcActivityGroupApply.getUserId()); pzcUser1.setMoney(pzcUser1.getMoney().add(pzcActivityGroupApply.getMoney()).add(money.subtract(new BigDecimal("20")))); pzcUserMapper.updateById(pzcUser1); //申请方获得发起方 扣除20派币 后的保证金 PzcOfficial pzcOfficial2 = new PzcOfficial(); pzcOfficial2.setIsRead(0L); pzcOfficial2.setFromUserId(null); pzcOfficial2.setToUserId(pzcActivityGroupApply.getUserId()); pzcOfficial2.setTitle("派之城提醒您:"); pzcOfficial2.setContent("您好,您在【" + pzcActivityGroupVo.getTitle() + "】组队活动中,由于对方未在活动结束前进行签到打卡,平台已对对方进行违约处理。对方的违约金【" + money.subtract(new BigDecimal("20")) + "】派币已纳入您的账户。您可以再次同意或申请其他用户。"); pzcOfficial2.setGroupId(groupId); pzcOfficial2.setActivityId(activityId); pzcOfficialMapper.insert(pzcOfficial2); wxUtils.insertUserHistory(pzcActivityGroupVo.getUserId(), pzcActivityGroupVo.getActivityId(), 3L, "活动时间到 自动取消组队 扣除保证金 【" + money + "】 派币", money.negate()); } } }; ScheduledExecutorUtils.scheduleTask(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(pzcActivityGroupVo.getActivityTime()), task, groupId, applyId); } catch (ParseException e) { log.info("创建定时任务失败,活动信息为 {}", JsonUtils.toJsonString(pzcActivityGroupVo)); } return R.ok(); } /** * 查询活动组队列表 */ @GetMapping("/list") public TableDataInfo list(PzcActivityGroupBo bo, PageQuery pageQuery) { log.info("组队大厅 查询条件是: {}", JsonUtils.toJsonString(bo)); return iPzcActivityGroupService.queryPageList(bo, pageQuery); } /** * 导出活动组队列表 */ @SaCheckPermission("system:activityGroup:export") @Log(title = "活动组队", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcActivityGroupBo bo, HttpServletResponse response) { List list = iPzcActivityGroupService.queryList(bo); ExcelUtil.exportExcel(list, "活动组队", PzcActivityGroupVo.class, response); } /** * 获取活动组队详细信息 * * @param groupId 主键 */ @GetMapping("/{groupId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long groupId) { return R.ok(iPzcActivityGroupService.queryById(groupId)); } @Autowired private PzcRegionMapper pzcRegionMapper; /** * 发起活动组队 */ @Log(title = "活动组队", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcActivityGroupBo bo) { Long userId = LoginHelper.getUserId(); bo.setUserId(userId); wxUtils.checkMgc(bo.getTitle());//校验敏感词 //校验活动是否存在 if (!iPzcActivityGroupService.checkActivity(bo.getActivityId()) && bo.getActivityId() != 0) { //如果不是城市活动 并且活动id不为0 则校验活动是否存在 return R.fail("活动不存在"); } PzcRegion pzcRegion = pzcRegionMapper.selectById(bo.getRegion()); //校验城市是否存在 只对派对生效 if (pzcRegion == null && pzcActivityMapper.selectById(bo.getActivityId()).getClassify() != 0) { log.info("传入的城市Id is {} ", bo.getRegion()); return R.fail("当前城市不存在"); } if(bo.getMoney().compareTo(new BigDecimal(99))<0) { return R.fail("派币保证金至少为99"); } //检验自己是否已经在此城市里发起过组队 if (bo.getActivityId() == 0) { //如果是城市活动 则校验是否已经发起过组队 List groups = pzcActivityGroupMapper.selectList(new QueryWrapper(). eq("user_id", userId).eq("activity_id", 0).eq("region", bo.getRegion()).eq("status", 0)); if (groups.size() != 0) { log.info("用户id为:{} 在城市id为:{} 发起过组队 {}", userId, bo.getRegion(), JsonUtils.toJsonString(groups)); return R.fail("您已经在此城市发起过组队了"); } bo.setActivityName("【" + pzcRegion.getName() + "】"); } else { //如果是活动 则校验是否已经发起过组队 //并且状态不为已结束 List groups = pzcActivityGroupMapper.selectList(new QueryWrapper().eq("user_id", userId).eq("activity_id", bo.getActivityId()).eq("status", 0)); if (groups.size() != 0) { log.info("用户id为:{} 在活动id为:{} 发起过组队 {}", userId, bo.getActivityId(), JsonUtils.toJsonString(groups)); return R.fail("您已经在此活动发起过组队了"); } bo.setActivityName(pzcActivityMapper.selectById(bo.getActivityId()).getTitle()); } // 校验保证金 PzcUser pzcUser = pzcUserMapper.selectById(userId); if (pzcUser.getMoney().compareTo(bo.getMoney()) < 0 || bo.getMoney().compareTo(new BigDecimal(99)) < 0) { return R.fail("保证金不足 至少拥有99个派币"); } return toAjax(iPzcActivityGroupService.insertByBo(bo)); } /** * 修改活动组队 */ @Log(title = "活动组队", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcActivityGroupBo bo) { bo.setUserId(LoginHelper.getUserId()); if(bo.getMoney().compareTo(new BigDecimal(99))<0) { return R.fail("保证金至少为99派币"); } //判断是否在组队进程中 判断组队状态 //获取我的申请列表 List applies = pzcActivityGroupApplyMapper.selectList(new QueryWrapper().eq("group_id", bo.getGroupId())); //判断是否有正在进行中的订单 applies.forEach( a -> { if (a.getApplyStatus() != -1 && a.getApplyStatus() != 0 && a.getApplyStatus() != 1) { throw new RuntimeException("当前活动处于" + wxUtils.applyStatus(a.getApplyStatus()) + " 无法修改"); } } ); return toAjax(iPzcActivityGroupService.updateByBo(bo)); } /** * 删除活动组队 * * @param groupIds 主键串 */ @SaCheckPermission("system:activityGroup:remove") @Log(title = "活动组队", businessType = BusinessType.DELETE) @DeleteMapping("/{groupIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] groupIds) { return toAjax(iPzcActivityGroupService.deleteWithValidByIds(Arrays.asList(groupIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcArtistController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcArtistVo; import top.flya.system.domain.bo.PzcArtistBo; import top.flya.system.service.IPzcArtistService; import top.flya.common.core.page.TableDataInfo; /** * 艺人 * * @author flya * @date 2023-06-01 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/artist") public class PzcArtistController extends BaseController { private final IPzcArtistService iPzcArtistService; /** * 查询艺人列表 */ @SaCheckPermission("system:artist:list") @GetMapping("/list") public TableDataInfo list(PzcArtistBo bo, PageQuery pageQuery) { return iPzcArtistService.queryPageList(bo, pageQuery); } /** * 导出艺人列表 */ @SaCheckPermission("system:artist:export") @Log(title = "艺人", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcArtistBo bo, HttpServletResponse response) { List list = iPzcArtistService.queryList(bo); ExcelUtil.exportExcel(list, "艺人", PzcArtistVo.class, response); } /** * 获取艺人详细信息 * * @param artistId 主键 */ @SaCheckPermission("system:artist:query") @GetMapping("/{artistId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long artistId) { return R.ok(iPzcArtistService.queryById(artistId)); } /** * 新增艺人 */ @SaCheckPermission("system:artist:add") @Log(title = "艺人", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcArtistBo bo) { return toAjax(iPzcArtistService.insertByBo(bo)); } /** * 修改艺人 */ @SaCheckPermission("system:artist:edit") @Log(title = "艺人", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcArtistBo bo) { return toAjax(iPzcArtistService.updateByBo(bo)); } /** * 删除艺人 * * @param artistIds 主键串 */ @SaCheckPermission("system:artist:remove") @Log(title = "艺人", businessType = BusinessType.DELETE) @DeleteMapping("/{artistIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] artistIds) { return toAjax(iPzcArtistService.deleteWithValidByIds(Arrays.asList(artistIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcIntroController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.bo.PzcIntroBo; import top.flya.system.domain.vo.PzcIntroVo; import top.flya.system.service.IPzcIntroService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; /** * 活动介绍 * * @author ruoyi * @date 2023-08-04 */ @Validated @RequiredArgsConstructor @RestController @Slf4j @RequestMapping("/system/intro") public class PzcIntroController extends BaseController { private final IPzcIntroService iPzcIntroService; /** * 查询活动介绍列表 */ @SaCheckPermission("system:intro:list") @GetMapping("/list") public TableDataInfo list(PzcIntroBo bo, PageQuery pageQuery) { return iPzcIntroService.queryPageList(bo, pageQuery); } /** * 导出活动介绍列表 */ @SaCheckPermission("system:intro:export") @Log(title = "活动介绍", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcIntroBo bo, HttpServletResponse response) { List list = iPzcIntroService.queryList(bo); ExcelUtil.exportExcel(list, "活动介绍", PzcIntroVo.class, response); } /** * 获取活动介绍详细信息 * * @param introId 主键 */ @SaCheckPermission("system:intro:query") @GetMapping("/{introId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long introId) { return R.ok(iPzcIntroService.queryById(introId)); } /** * 新增活动介绍 */ @SaCheckPermission("system:intro:add") @Log(title = "活动介绍", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcIntroBo bo) { return toAjax(iPzcIntroService.insertByBo(bo)); } /** * 修改活动介绍 */ @SaCheckPermission("system:intro:edit") @Log(title = "活动介绍", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcIntroBo bo) { log.info("bo:{}", JsonUtils.toJsonString(bo)); return toAjax(iPzcIntroService.updateByBo(bo)); } /** * 删除活动介绍 * * @param introIds 主键串 */ @SaCheckPermission("system:intro:remove") @Log(title = "活动介绍", businessType = BusinessType.DELETE) @DeleteMapping("/{introIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] introIds) { return toAjax(iPzcIntroService.deleteWithValidByIds(Arrays.asList(introIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOfficialController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.bo.PzcOfficialBo; import top.flya.system.domain.vo.PzcOfficialVo; import top.flya.system.service.IPzcOfficialService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; /** * 官方消息 * * @author ruoyi * @date 2023-07-27 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/official") public class PzcOfficialController extends BaseController { private final IPzcOfficialService iPzcOfficialService; /** * 查询官方 未读消息列表 */ @GetMapping("/list") public TableDataInfo list(PzcOfficialBo bo, PageQuery pageQuery) { bo.setToUserId(LoginHelper.getUserId()); pageQuery.setOrderByColumn("create_time"); pageQuery.setIsAsc("desc"); return iPzcOfficialService.queryPageList(bo, pageQuery); } /** * 已读消息 * 如果officialId为空,则表示全部已读 * @param officialId * @return */ @PostMapping("/read") public R read(@RequestParam(value = "officialId",required = false) Integer officialId) { return R.ok(iPzcOfficialService.read(officialId)); } /** * 导出官方消息列表 */ @SaCheckPermission("system:official:export") @Log(title = "官方消息", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcOfficialBo bo, HttpServletResponse response) { List list = iPzcOfficialService.queryList(bo); ExcelUtil.exportExcel(list, "官方消息", PzcOfficialVo.class, response); } /** * 获取官方消息详细信息 * * @param officialId 主键 */ @SaCheckPermission("system:official:query") @GetMapping("/{officialId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long officialId) { return R.ok(iPzcOfficialService.queryById(officialId)); } /** * 新增官方消息 */ @SaCheckPermission("system:official:add") @Log(title = "官方消息", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcOfficialBo bo) { return toAjax(iPzcOfficialService.insertByBo(bo)); } /** * 修改官方消息 */ @SaCheckPermission("system:official:edit") @Log(title = "官方消息", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcOfficialBo bo) { return toAjax(iPzcOfficialService.updateByBo(bo)); } /** * 删除官方消息 * * @param officialIds 主键串 */ @SaCheckPermission("system:official:remove") @Log(title = "官方消息", businessType = BusinessType.DELETE) @DeleteMapping("/{officialIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] officialIds) { return toAjax(iPzcOfficialService.deleteWithValidByIds(Arrays.asList(officialIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOrderController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcOrderVo; import top.flya.system.domain.bo.PzcOrderBo; import top.flya.system.service.IPzcOrderService; import top.flya.common.core.page.TableDataInfo; /** * 订单 * * @author ruoyi * @date 2023-07-09 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/pzc_order") public class PzcOrderController extends BaseController { private final IPzcOrderService iPzcOrderService; /** * 查询订单列表 */ @SaCheckPermission("system:pzc_order:list") @GetMapping("/list") public TableDataInfo list(PzcOrderBo bo, PageQuery pageQuery) { return iPzcOrderService.queryPageList(bo, pageQuery); } /** * 导出订单列表 */ @SaCheckPermission("system:pzc_order:export") @Log(title = "订单", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcOrderBo bo, HttpServletResponse response) { List list = iPzcOrderService.queryList(bo); ExcelUtil.exportExcel(list, "订单", PzcOrderVo.class, response); } /** * 获取订单详细信息 * * @param orderId 主键 */ @SaCheckPermission("system:pzc_order:query") @GetMapping("/{orderId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long orderId) { return R.ok(iPzcOrderService.queryById(orderId)); } /** * 新增订单 */ @SaCheckPermission("system:pzc_order:add") @Log(title = "订单", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcOrderBo bo) { return toAjax(iPzcOrderService.insertByBo(bo)); } /** * 修改订单 */ @SaCheckPermission("system:pzc_order:edit") @Log(title = "订单", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcOrderBo bo) { return toAjax(iPzcOrderService.updateByBo(bo)); } /** * 删除订单 * * @param orderIds 主键串 */ @SaCheckPermission("system:pzc_order:remove") @Log(title = "订单", businessType = BusinessType.DELETE) @DeleteMapping("/{orderIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] orderIds) { return toAjax(iPzcOrderService.deleteWithValidByIds(Arrays.asList(orderIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOrganizerController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcOrganizerVo; import top.flya.system.domain.bo.PzcOrganizerBo; import top.flya.system.service.IPzcOrganizerService; import top.flya.common.core.page.TableDataInfo; /** * 活动主办方 * * @author ruoyi * @date 2023-06-02 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/organizer") public class PzcOrganizerController extends BaseController { private final IPzcOrganizerService iPzcOrganizerService; /** * 查询活动主办方列表 */ @SaCheckPermission("system:organizer:list") @GetMapping("/list") public TableDataInfo list(PzcOrganizerBo bo, PageQuery pageQuery) { return iPzcOrganizerService.queryPageList(bo, pageQuery); } /** * 导出活动主办方列表 */ @SaCheckPermission("system:organizer:export") @Log(title = "活动主办方", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcOrganizerBo bo, HttpServletResponse response) { List list = iPzcOrganizerService.queryList(bo); ExcelUtil.exportExcel(list, "活动主办方", PzcOrganizerVo.class, response); } /** * 获取活动主办方详细信息 * * @param organizerId 主键 */ @SaCheckPermission("system:organizer:query") @GetMapping("/{organizerId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long organizerId) { return R.ok(iPzcOrganizerService.queryById(organizerId)); } /** * 新增活动主办方 */ @SaCheckPermission("system:organizer:add") @Log(title = "活动主办方", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcOrganizerBo bo) { return toAjax(iPzcOrganizerService.insertByBo(bo)); } /** * 修改活动主办方 */ @SaCheckPermission("system:organizer:edit") @Log(title = "活动主办方", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcOrganizerBo bo) { return toAjax(iPzcOrganizerService.updateByBo(bo)); } /** * 删除活动主办方 * * @param organizerIds 主键串 */ @SaCheckPermission("system:organizer:remove") @Log(title = "活动主办方", businessType = BusinessType.DELETE) @DeleteMapping("/{organizerIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] organizerIds) { return toAjax(iPzcOrganizerService.deleteWithValidByIds(Arrays.asList(organizerIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcOrganizerTicketController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcOrganizerTicketVo; import top.flya.system.domain.bo.PzcOrganizerTicketBo; import top.flya.system.service.IPzcOrganizerTicketService; import top.flya.common.core.page.TableDataInfo; /** * 主办方票务 * * @author ruoyi * @date 2023-07-22 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/organizerTicket") public class PzcOrganizerTicketController extends BaseController { private final IPzcOrganizerTicketService iPzcOrganizerTicketService; /** * 查询主办方票务列表 */ @SaCheckPermission("system:organizerTicket:list") @GetMapping("/list") public TableDataInfo list(PzcOrganizerTicketBo bo, PageQuery pageQuery) { return iPzcOrganizerTicketService.queryPageList(bo, pageQuery); } /** * 导出主办方票务列表 */ @SaCheckPermission("system:organizerTicket:export") @Log(title = "主办方票务", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcOrganizerTicketBo bo, HttpServletResponse response) { List list = iPzcOrganizerTicketService.queryList(bo); ExcelUtil.exportExcel(list, "主办方票务", PzcOrganizerTicketVo.class, response); } /** * 获取主办方票务详细信息 * * @param organizerTicketId 主键 */ @SaCheckPermission("system:organizerTicket:query") @GetMapping("/{organizerTicketId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long organizerTicketId) { return R.ok(iPzcOrganizerTicketService.queryById(organizerTicketId)); } /** * 新增主办方票务 */ @SaCheckPermission("system:organizerTicket:add") @Log(title = "主办方票务", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcOrganizerTicketBo bo) { return toAjax(iPzcOrganizerTicketService.insertByBo(bo)); } /** * 修改主办方票务 */ @SaCheckPermission("system:organizerTicket:edit") @Log(title = "主办方票务", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcOrganizerTicketBo bo) { return toAjax(iPzcOrganizerTicketService.updateByBo(bo)); } /** * 删除主办方票务 * * @param organizerTicketIds 主键串 */ @SaCheckPermission("system:organizerTicket:remove") @Log(title = "主办方票务", businessType = BusinessType.DELETE) @DeleteMapping("/{organizerTicketIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] organizerTicketIds) { return toAjax(iPzcOrganizerTicketService.deleteWithValidByIds(Arrays.asList(organizerTicketIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcRegionController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.bo.PzcRegionBo; import top.flya.system.domain.vo.PzcRegionVo; import top.flya.system.mapper.RegionTreeMapper; import top.flya.system.service.IPzcRegionService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; /** * 地区 * * @author ruoyi * @date 2023-07-22 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/region") public class PzcRegionController extends BaseController { private final IPzcRegionService iPzcRegionService; private final RegionTreeMapper regionTreeMapper; // /** // * 查询地区列表 // */ // @SaCheckPermission("system:region:list") // @GetMapping("/list") // public TableDataInfo list(PzcRegionBo bo, PageQuery pageQuery) { // return iPzcRegionService.queryPageList(bo, pageQuery); // } @GetMapping("/list") public R list(@RequestParam(required = false) String regionName) { PzcRegionBo bo = new PzcRegionBo(); bo.setName(regionName); List pzcRegionVos = iPzcRegionService.queryList(bo); //根据 pzcRegionVos 生成树形结构 base 字段相同的为同一级 return R.ok(regionTreeMapper.buildTree(pzcRegionVos)); } /** * 导出地区列表 */ @SaCheckPermission("system:region:export") @Log(title = "地区", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcRegionBo bo, HttpServletResponse response) { List list = iPzcRegionService.queryList(bo); ExcelUtil.exportExcel(list, "地区", PzcRegionVo.class, response); } /** * 获取地区详细信息 * * @param regionId 主键 */ @SaCheckPermission("system:region:query") @GetMapping("/{regionId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long regionId) { return R.ok(iPzcRegionService.queryById(regionId)); } /** * 新增地区 */ @SaCheckPermission("system:region:add") @Log(title = "地区", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcRegionBo bo) { return toAjax(iPzcRegionService.insertByBo(bo)); } /** * 修改地区 */ @SaCheckPermission("system:region:edit") @Log(title = "地区", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcRegionBo bo) { return toAjax(iPzcRegionService.updateByBo(bo)); } /** * 删除地区 * * @param regionIds 主键串 */ @SaCheckPermission("system:region:remove") @Log(title = "地区", businessType = BusinessType.DELETE) @DeleteMapping("/{regionIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] regionIds) { return toAjax(iPzcRegionService.deleteWithValidByIds(Arrays.asList(regionIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcTagController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcTagVo; import top.flya.system.domain.bo.PzcTagBo; import top.flya.system.service.IPzcTagService; import top.flya.common.core.page.TableDataInfo; /** * 活动标签 * * @author ruoyi * @date 2023-06-02 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/tag") public class PzcTagController extends BaseController { private final IPzcTagService iPzcTagService; /** * 查询活动标签列表 */ @SaCheckPermission("system:tag:list") @GetMapping("/list") public TableDataInfo list(PzcTagBo bo, PageQuery pageQuery) { return iPzcTagService.queryPageList(bo, pageQuery); } /** * 导出活动标签列表 */ @SaCheckPermission("system:tag:export") @Log(title = "活动标签", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcTagBo bo, HttpServletResponse response) { List list = iPzcTagService.queryList(bo); ExcelUtil.exportExcel(list, "活动标签", PzcTagVo.class, response); } /** * 获取活动标签详细信息 * * @param tagId 主键 */ @SaCheckPermission("system:tag:query") @GetMapping("/{tagId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long tagId) { return R.ok(iPzcTagService.queryById(tagId)); } /** * 新增活动标签 */ @SaCheckPermission("system:tag:add") @Log(title = "活动标签", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcTagBo bo) { return toAjax(iPzcTagService.insertByBo(bo)); } /** * 修改活动标签 */ @SaCheckPermission("system:tag:edit") @Log(title = "活动标签", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcTagBo bo) { return toAjax(iPzcTagService.updateByBo(bo)); } /** * 删除活动标签 * * @param tagIds 主键串 */ @SaCheckPermission("system:tag:remove") @Log(title = "活动标签", businessType = BusinessType.DELETE) @DeleteMapping("/{tagIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] tagIds) { return toAjax(iPzcTagService.deleteWithValidByIds(Arrays.asList(tagIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserCollectController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.common.BatchUtils; import top.flya.system.domain.PzcActivity; import top.flya.system.domain.bo.PzcUserCollectBo; import top.flya.system.domain.vo.PzcUserCollectVo; import top.flya.system.mapper.PzcActivityMapper; import top.flya.system.service.IPzcUserCollectService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotNull; import java.util.*; /** * 用户收藏活动 * * @author ruoyi * @date 2023-07-08 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/userCollect") @Slf4j public class PzcUserCollectController extends BaseController { private final IPzcUserCollectService iPzcUserCollectService; private final PzcActivityMapper pzcActivityMapper; private final StringRedisTemplate stringRedisTemplate; private final BatchUtils batchUtils; /** * 查询用户收藏活动列表 */ @GetMapping("/list") public R> list(PzcUserCollectBo bo, PageQuery pageQuery) { Set members = stringRedisTemplate.opsForSet().members("collect:" + getUserId()); if(members==null||members.isEmpty()) { return R.ok(); } List collect = new ArrayList<>(members); List pzcActivities = pzcActivityMapper.selectActivityByActivityIds(collect, bo.getType()); log.info("用户收藏活动列表 {}", JsonUtils.toJsonString(pzcActivities)); pzcActivities.stream().forEach( pzcActivity -> { pzcActivity.setCoverImage(pzcActivity.getCoverImage().contains("http")?pzcActivity.getCoverImage(): (batchUtils.getNewImageUrls(Collections.singletonList(pzcActivity.getCoverImage())).get(Long.parseLong((pzcActivity.getCoverImage()))))); } ); return R.ok(pzcActivities); } /** * 导出用户收藏活动列表 */ @SaCheckPermission("system:userCollect:export") @Log(title = "用户收藏活动", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcUserCollectBo bo, HttpServletResponse response) { List list = iPzcUserCollectService.queryList(bo); ExcelUtil.exportExcel(list, "用户收藏活动", PzcUserCollectVo.class, response); } /** * 获取用户收藏活动详细信息 * * @param collectId 主键 */ @GetMapping("/{collectId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long collectId) { return R.ok(iPzcUserCollectService.queryById(collectId)); } @GetMapping("/status") public R status(@RequestParam("activityId") Long activityId) { return R.ok(Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember("collect:" + getUserId(), activityId.toString()))); } /** * 新增用户收藏活动 这里改为存入Redis 加快响应速度 */ @Log(title = "用户收藏/取消活动", businessType = BusinessType.INSERT) @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcUserCollectBo bo) { log.info("用户收藏/取消活动 {}", JsonUtils.toJsonString(bo)); if(Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember("collect:" + getUserId(), bo.getActivityId().toString()))) { //取消收藏活动 stringRedisTemplate.opsForSet().remove("collect:" + getUserId(),bo.getActivityId().toString()); }else { stringRedisTemplate.opsForSet().add("collect:" + getUserId(),bo.getActivityId().toString()); } return R.ok("1"); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.bo.PzcUserBo; import top.flya.system.domain.bo.UpdateMoneyBo; import top.flya.system.domain.vo.PzcUserVo; import top.flya.system.service.IPzcUserService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; /** * 用户 * * @author ruoyi * @date 2023-07-09 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/pzc_user") public class PzcUserController extends BaseController { private final IPzcUserService iPzcUserService; /** * 查询用户列表 */ @SaCheckPermission("system:pzc_user:list") @GetMapping("/list") public TableDataInfo list(PzcUserBo bo, PageQuery pageQuery) { return iPzcUserService.queryPageList(bo, pageQuery); } /** * 导出用户列表 */ @SaCheckPermission("system:pzc_user:export") @Log(title = "用户", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcUserBo bo, HttpServletResponse response) { List list = iPzcUserService.queryList(bo); ExcelUtil.exportExcel(list, "用户", PzcUserVo.class, response); } /** * 获取用户详细信息 * * @param userId 主键 */ @SaCheckPermission("system:pzc_user:query") @GetMapping("/{userId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long userId) { return R.ok(iPzcUserService.queryById(userId)); } /** * 新增用户 */ @SaCheckPermission("system:pzc_user:add") @Log(title = "用户", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcUserBo bo) { return toAjax(iPzcUserService.insertByBo(bo)); } @PostMapping("/updateMoney") public R updateMoney(@Validated(AddGroup.class) @RequestBody UpdateMoneyBo bo) { return toAjax(iPzcUserService.updateMoney(bo)); } /** * 修改用户 */ @SaCheckPermission("system:pzc_user:edit") @Log(title = "用户", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcUserBo bo) { return toAjax(iPzcUserService.updateByBo(bo)); } /** * 删除用户 * * @param userIds 主键串 */ @SaCheckPermission("system:pzc_user:remove") @Log(title = "用户", businessType = BusinessType.DELETE) @DeleteMapping("/{userIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] userIds) { return toAjax(iPzcUserService.deleteWithValidByIds(Arrays.asList(userIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserHistoryController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.bo.PzcUserHistoryBo; import top.flya.system.domain.vo.PzcUserHistoryVo; import top.flya.system.service.IPzcUserHistoryService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; /** * 用户操作历史记录 * * @author ruoyi * @date 2023-07-06 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/userHistory") public class PzcUserHistoryController extends BaseController { private final IPzcUserHistoryService iPzcUserHistoryService; /** * 查询用户操作历史记录列表 */ @GetMapping("/list") public TableDataInfo list(PzcUserHistoryBo bo, PageQuery pageQuery) { bo.setUserId(LoginHelper.getUserId()); pageQuery.setOrderByColumn("create_time"); pageQuery.setIsAsc("desc"); return iPzcUserHistoryService.queryPageList(bo, pageQuery); } /** * 导出用户操作历史记录列表 */ @SaCheckPermission("system:userHistory:export") @Log(title = "用户操作历史记录", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcUserHistoryBo bo, HttpServletResponse response) { List list = iPzcUserHistoryService.queryList(bo); ExcelUtil.exportExcel(list, "用户操作历史记录", PzcUserHistoryVo.class, response); } /** * 获取用户操作历史记录详细信息 * * @param historyId 主键 */ @SaCheckPermission("system:userHistory:query") @GetMapping("/{historyId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long historyId) { return R.ok(iPzcUserHistoryService.queryById(historyId)); } /** * 新增用户操作历史记录 */ @SaCheckPermission("system:userHistory:add") @Log(title = "用户操作历史记录", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcUserHistoryBo bo) { return toAjax(iPzcUserHistoryService.insertByBo(bo)); } /** * 修改用户操作历史记录 */ @SaCheckPermission("system:userHistory:edit") @Log(title = "用户操作历史记录", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcUserHistoryBo bo) { return toAjax(iPzcUserHistoryService.updateByBo(bo)); } /** * 删除用户操作历史记录 * * @param historyIds 主键串 */ @SaCheckPermission("system:userHistory:remove") @Log(title = "用户操作历史记录", businessType = BusinessType.DELETE) @DeleteMapping("/{historyIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] historyIds) { return toAjax(iPzcUserHistoryService.deleteWithValidByIds(Arrays.asList(historyIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserPhotoController.java ================================================ package top.flya.system.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.PzcUserPhotoVo; import top.flya.system.domain.bo.PzcUserPhotoBo; import top.flya.system.service.IPzcUserPhotoService; import top.flya.common.core.page.TableDataInfo; /** * 用户资料相册 * * @author ruoyi * @date 2023-07-11 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/userPhoto") public class PzcUserPhotoController extends BaseController { private final IPzcUserPhotoService iPzcUserPhotoService; /** * 查询用户资料相册列表 */ @GetMapping("/list") public TableDataInfo list(PzcUserPhotoBo bo, PageQuery pageQuery) { if(bo.getUserId()==null) { bo.setUserId(LoginHelper.getUserId()); } return iPzcUserPhotoService.queryPageList(bo, pageQuery); } /** * 导出用户资料相册列表 */ @Log(title = "用户资料相册", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcUserPhotoBo bo, HttpServletResponse response) { List list = iPzcUserPhotoService.queryList(bo); ExcelUtil.exportExcel(list, "用户资料相册", PzcUserPhotoVo.class, response); } /** * 获取用户资料相册详细信息 * * @param photoId 主键 */ @GetMapping("/{photoId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long photoId) { return R.ok(iPzcUserPhotoService.queryById(photoId)); } /** * 新增用户资料相册 */ @Log(title = "用户资料相册", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcUserPhotoBo bo) { bo.setUserId(LoginHelper.getUserId()); return toAjax(iPzcUserPhotoService.insertByBo(bo)); } /** * 修改用户资料相册 */ @Log(title = "用户资料相册", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcUserPhotoBo bo) { return toAjax(iPzcUserPhotoService.updateByBo(bo)); } /** * 删除用户资料相册 * * @param photoIds 主键串 */ @Log(title = "用户资料相册", businessType = BusinessType.DELETE) @DeleteMapping("/{photoIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] photoIds) { return toAjax(iPzcUserPhotoService.deleteWithValidByIds(Arrays.asList(photoIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcUserTalkController.java ================================================ package top.flya.system.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.JsonUtils; import top.flya.system.domain.PzcUser; import top.flya.system.domain.PzcUserTalk; import top.flya.system.domain.bo.PzcUserTalkBo; import top.flya.system.domain.vo.PzcUserTalkVo; import top.flya.system.mapper.PzcUserMapper; import top.flya.system.mapper.PzcUserPhotoMapper; import top.flya.system.mapper.PzcUserTalkMapper; import top.flya.system.service.IPzcUserTalkService; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static top.flya.system.config.ClientCache.concurrentHashMap; /** * 用户聊天 * * @author ruoyi * @date 2023-07-14 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/userTalk") @Slf4j public class PzcUserTalkController extends BaseController { private final IPzcUserTalkService iPzcUserTalkService; private final PzcUserTalkMapper pzcUserTalkMapper; private final PzcUserMapper pzcUserMapper; private final PzcUserPhotoMapper pzcUserPhotoMapper; @PostMapping("/deleteByUserId") //删除聊天记录 public R deleteByUserId(@RequestParam("userId") String userId, @RequestParam("groupId") Long groupId) { Long my = LoginHelper.getUserId(); pzcUserTalkMapper.delete(new QueryWrapper(). eq("group_id", groupId). and( i -> i.eq("user_id", my). eq("from_user_id", userId).or(). eq("user_id", my).eq("to_user_id", userId))); return R.ok(); } /** * 用户在线状态 * * @param userId * @return */ @GetMapping("/live") public R liveStatus(@RequestParam("userId") String userId) { Boolean liveStatus = false; if (concurrentHashMap.get(userId) != null) { liveStatus = true; } PzcUser pzcUser = pzcUserMapper.selectById(userId); Map result = new java.util.HashMap<>(); result.put("liveStatus", liveStatus.toString()); result.put("userId", userId); result.put("nickName", pzcUser.getNickname()); result.put("address", pzcUser.getAddress()); result.put("sex", String.valueOf(pzcUser.getSex())); result.put("info", pzcUser.getIntro()); result.put("avatar", pzcUser.getAvatar()); result.put("age", pzcUser.getAge()); result.put("level", pzcUser.getUserLevel()); // 查询用户相册 result.put("photo", pzcUserPhotoMapper.selectList(new QueryWrapper().eq("user_id", userId)).stream().map(top.flya.system.domain.PzcUserPhoto::getUrl).collect(Collectors.toList())); return R.ok(result); } /** * 一键已读 * * @param userId * @return */ @PostMapping("/read") public R read(@RequestParam("userId") String userId) { List pzcUserTalks = pzcUserTalkMapper.selectList(new QueryWrapper().eq("from_user_id", userId).eq("to_user_id", LoginHelper.getUserId())).stream() .filter(pzcUserTalk -> pzcUserTalk.getMessageStatus() == 0) .peek(pzcUserTalk -> pzcUserTalk.setMessageStatus(1L)) .collect(Collectors.toList()); log.info("pzcUserTalks:{}", JsonUtils.toJsonString(pzcUserTalks)); if (pzcUserTalks.size() == 0) { return R.ok(true); } boolean b = pzcUserTalkMapper.updateBatchById(pzcUserTalks); return R.ok(b); } /** * 我的聊天列表 *

* 1. 按照 最后聊天的时间倒序排列 * 2. 展示 最新一条聊天记录 以及未读消息条数 */ @GetMapping("/myList") public TableDataInfo myList(PzcUserTalkBo bo, PageQuery pageQuery) { return iPzcUserTalkService.queryMyPageList(bo, pageQuery); } /** * 查询用户聊天列表 */ @GetMapping("/list") public TableDataInfo list(PzcUserTalkBo bo, PageQuery pageQuery) { bo.setFromUserId(LoginHelper.getUserId()); bo.setUserId(LoginHelper.getUserId()); pageQuery.setIsAsc("desc"); pageQuery.setOrderByColumn("create_time"); return iPzcUserTalkService.queryPageList(bo, pageQuery); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/PzcViewPagerController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.bo.PzcViewPagerBo; import top.flya.system.domain.vo.PzcViewPagerVo; import top.flya.system.service.IPzcViewPagerService; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.List; /** * 轮播图 * * @author ruoyi * @date 2023-05-23 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/viewPager") public class PzcViewPagerController extends BaseController { private final IPzcViewPagerService iPzcViewPagerService; /** * 查询轮播图列表 */ @GetMapping("/list") public TableDataInfo list(PzcViewPagerBo bo, PageQuery pageQuery) { return iPzcViewPagerService.queryPageList(bo, pageQuery); } /** * 导出轮播图列表 */ @SaCheckPermission("system:viewPager:export") @Log(title = "轮播图", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(PzcViewPagerBo bo, HttpServletResponse response) { List list = iPzcViewPagerService.queryList(bo); ExcelUtil.exportExcel(list, "轮播图", PzcViewPagerVo.class, response); } /** * 获取轮播图详细信息 * * @param viewPagerId 主键 */ @GetMapping("/{viewPagerId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Integer viewPagerId) { return R.ok(iPzcViewPagerService.queryById(viewPagerId)); } /** * 新增轮播图 */ @SaCheckPermission("system:viewPager:add") @Log(title = "轮播图", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PzcViewPagerBo bo) { return toAjax(iPzcViewPagerService.insertByBo(bo)); } /** * 修改轮播图 */ @SaCheckPermission("system:viewPager:edit") @Log(title = "轮播图", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PzcViewPagerBo bo) { return toAjax(iPzcViewPagerService.updateByBo(bo)); } /** * 删除轮播图 * * @param viewPagerIds 主键串 */ @SaCheckPermission("system:viewPager:remove") @Log(title = "轮播图", businessType = BusinessType.DELETE) @DeleteMapping("/{viewPagerIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Integer[] viewPagerIds) { return toAjax(iPzcViewPagerService.deleteWithValidByIds(Arrays.asList(viewPagerIds), true)); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/controller/WxUserController.java ================================================ package top.flya.system.controller; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.alibaba.excel.util.DateUtils; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.wechat.pay.contrib.apache.httpclient.util.AesUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import top.flya.common.constant.Constants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.R; import top.flya.common.core.domain.event.LogininforEvent; import top.flya.common.core.domain.model.XcxLoginUser; import top.flya.common.enums.DeviceType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.MessageUtils; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.system.domain.*; import top.flya.system.domain.bo.PayOrderBo; import top.flya.system.domain.bo.PzcUserBo; import top.flya.system.domain.bo.SuccessCallBackObjBo; import top.flya.system.domain.vo.PzcUserHistoryVo; import top.flya.system.handel.WxPayInitHandel; import top.flya.system.mapper.*; import top.flya.system.utils.CreateSign; import top.flya.system.utils.WxUtils; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.math.BigDecimal; import java.security.GeneralSecurityException; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @Validated @RequiredArgsConstructor @RestController @RequestMapping("/wx/user") @Slf4j public class WxUserController extends BaseController { @Value("${wx.appId}") private String appId; @Value("${wx.appSecret}") private String secret; @Value("${sa-token.token-prefix}") private String tokenPrefix; @Value("${wx.merchantId}") private String mchId; @Value("${wx.api3}") private String api3; @Value("${wx.notifyUrl}") private String notifyUrl; @Value("${api.school}") private String schoolUrl; @Value("${api.apiKey}") private String apiKey; private final PzcUserMapper userMapper; private final WxUtils wxUtils; private final PzcUserHistoryMapper userHistoryMapper; @Autowired private WxPayInitHandel wxPayInitHandel; @Autowired private CreateSign createSign; @Autowired private PzcOrderMapper orderMapper; @Autowired private PzcOfficialMapper officialMapper; @Autowired private PzcUserTalkMapper talkMapper; @Autowired private SysUserMapper sysUserMapper; @Value("${neteaseCloudMusicApi}") private String neteaseCloudMusicApi; @GetMapping("/filterKeyWords") public R filterKeyWords(@RequestParam("msg") String msg) { wxUtils.checkMgc(msg); return R.ok(); } @GetMapping("/music") public R music() { String url = neteaseCloudMusicApi + sysUserMapper.selectUserById(1L).getNickName(); log.info("url is {}", url); String result = HttpUtil.get(url); JSONObject jsonObject = JSONObject.parseObject(result); return R.ok(jsonObject.getJSONArray("data").getJSONObject(0).getString("url")); } @GetMapping("/notRead") // 获取首页 未读消息 (红点点) public R notRead() { PzcUser user = wxUtils.checkUser(); List pzcOfficials = officialMapper.selectList(new QueryWrapper().eq("to_user_id", user.getUserId()).eq("is_read", 0)); Integer size1 = pzcOfficials.size(); Integer size2 = talkMapper.selectList(new QueryWrapper().eq("to_user_id", user.getUserId()).eq("message_status", 0)).size(); return R.ok(Math.min(size1 + size2, 99)); } @PostMapping("/login") // 登录 public R login(@RequestBody @Validated PzcUserBo loginUser) { String tokenValue = userLogin(loginUser); return (tokenValue != null) ? R.ok(tokenPrefix + " " + tokenValue) : R.fail("登录失败"); } @GetMapping("/getSchoolList") public R getSchoolList(@RequestParam("schoolName") String schoolName) { String baseUrl = schoolUrl + schoolName + "&" + apiKey; log.info("baseUrl is {}", baseUrl); String result = HttpUtil.get(baseUrl); return R.ok(JSONObject.parseObject(result).get("data")); } @GetMapping("/userInfo") // 获取用户信息 public R userInfo() { return R.ok(wxUtils.checkUser()); } @PostMapping("/updateUserInfo") // 更新用户信息 public R updateUserInfo(@RequestBody PzcUserBo pzcUserBo) { log.info("更新用户信息: pzcUserBo is {}", pzcUserBo); PzcUser user = wxUtils.checkUser(); //获取现在时间和一年前的时间 并格式化 String nowTime = DateUtils.format(new Date()); String lastYearNow = LocalDateTime.of(LocalDate.now().minusYears(1), LocalDateTime.now().toLocalTime()).toString(); log.info("nowTime is {} , lastYearNow is {}", nowTime, lastYearNow); if (pzcUserBo.getNickname() != null && !user.getNickname().equals(pzcUserBo.getNickname())) { //判断用户是否之前一年内是否更新过昵称 List pzcUserHistoryVos = userHistoryMapper. selectVoList(new QueryWrapper().eq("user_id", user.getUserId()).eq("type", 0).like("message", "%昵称%") .between("create_time", lastYearNow, nowTime)); if (!pzcUserHistoryVos.isEmpty()) { return R.fail("一年内只能修改一次昵称"); } else { wxUtils.checkMgc(pzcUserBo.getNickname()); //更新用户信息 user.setNickname(pzcUserBo.getNickname()); userMapper.updateById(user); //存入用户历史记录 wxUtils.insertUserHistory(user.getUserId(), 0L, 0L, "昵称修改为" + pzcUserBo.getNickname(), null); return R.ok(userMapper.selectById(user.getUserId())); } } else { pzcUserBo.setMoney(user.getMoney());//余额不允许修改 pzcUserBo.setUserId(user.getUserId());//用户id不允许修改 pzcUserBo.setRealname(user.getRealname());//真实姓名不允许修改 pzcUserBo.setPhone(user.getPhone());//手机号不允许修改 pzcUserBo.setOpenid(user.getOpenid());//openid不允许修改 pzcUserBo.setExemptCancel(user.getExemptCancel());//免责不允许修改 Map map = BeanUtil.beanToMap(pzcUserBo); //存入用户历史记录 wxUtils.insertUserHistory(user.getUserId(), 0L, 0L, "更新用户其他信息", null); return R.ok(updateUser(map, user)); } } @PostMapping("/recharge") // 充值 @Transactional public R createOrder(@RequestBody @Validated PayOrderBo payOrder) throws Exception { PzcUser user = wxUtils.checkUser(); String openId = user.getOpenid(); CloseableHttpClient httpClient = wxPayInitHandel.setup(); //请求URL HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"); HashMap amount = new HashMap<>(); amount.put("total", payOrder.getCount()); amount.put("currency", "CNY"); HashMap payer = new HashMap<>(); payer.put("openid", openId); String orderNum = IdUtil.getSnowflakeNextIdStr(); HashMap toData = new HashMap<>(); toData.put("amount", amount); toData.put("mchid", mchId); toData.put("description", "派币充值订单"); toData.put("notify_url", notifyUrl); toData.put("payer", payer); toData.put("attach", orderNum); toData.put("out_trade_no", orderNum); toData.put("goods_tag", "WXG"); toData.put("appid", appId); String s = JSONObject.toJSONString(toData); log.info("订单: " + s); StringEntity entity = new StringEntity(s, "utf-8"); entity.setContentType("application/json"); httpPost.setEntity(entity); httpPost.setHeader("Accept", "application/json"); //完成签名并执行请求 CloseableHttpResponse response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 200) { String result = "prepay_id=" + JSONObject.parseObject(EntityUtils.toString(response.getEntity())).get("prepay_id").toString(); response.close(); //关闭连接 wxPayInitHandel.after(httpClient); String nonceStr = "5K8264ILTKCH16CQ2502SI8ZNMTM67VS"; long timestamp = System.currentTimeMillis() / 1000; String paySign = createSign.getSign(appId, timestamp, nonceStr, result); HashMap map = new HashMap<>(); map.put("timestamp", String.valueOf(timestamp)); map.put("nonceStr", nonceStr); map.put("package", result); map.put("signType", "RSA"); map.put("paySign", paySign); map.put("orderNum", orderNum); //创建未支付的充值订单 PzcOrder pzcRechargeOrder = new PzcOrder(); pzcRechargeOrder.setActivityId(null); pzcRechargeOrder.setMoney(BigDecimal.valueOf(payOrder.getCount()).divide(BigDecimal.valueOf(100))); pzcRechargeOrder.setOrderStatus(0L); pzcRechargeOrder.setType(0L); pzcRechargeOrder.setOutOrderNum(orderNum); pzcRechargeOrder.setIntro("派币充值订单"); pzcRechargeOrder.setTitle("派币充值"); pzcRechargeOrder.setUserId(user.getUserId()); orderMapper.insert(pzcRechargeOrder); return R.ok(map); } else if (statusCode == 204) { log.info("success"); response.close(); //关闭连接 wxPayInitHandel.after(httpClient); return R.ok("成功但未返回预支付订单号"); } else { log.info("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity())); String result = EntityUtils.toString(response.getEntity()); response.close(); //关闭连接 wxPayInitHandel.after(httpClient); return R.fail("创建预支付订单失败: " + result); } } /**/ @RequestMapping("/callback") @Transactional public R callback(HttpServletRequest request, @RequestBody SuccessCallBackObjBo obj) { log.info("进入支付回调啦~"); String associated_data = obj.getResource().getAssociated_data(); String nonce = obj.getResource().getNonce(); String ciphertext = obj.getResource().getCiphertext(); AesUtil aesUtil = new AesUtil(api3.getBytes()); try { String s = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext); JSONObject jsonObject = JSONObject.parseObject(s); jsonObject.forEach((k, v) -> log.info("k:" + k + " v:" + v + "\n")); String orderNum = jsonObject.getString("out_trade_no"); //更新订单状态和用户余额 PzcOrder pzcOrder = orderMapper.selectOne(new QueryWrapper().eq("out_order_num", orderNum)); if (pzcOrder == null) { return R.fail("订单不存在"); } if (pzcOrder.getOrderStatus() == 1) { return R.fail("订单已支付"); } pzcOrder.setOrderStatus(1L); orderMapper.updateById(pzcOrder); Long userId = pzcOrder.getUserId(); PzcUser user = userMapper.selectById(userId); user.setMoney(user.getMoney().add(pzcOrder.getMoney())); userMapper.updateById(user); wxUtils.insertUserHistory(user.getUserId(), 0L, 2L, "派币充值【" + pzcOrder.getMoney() + "】", pzcOrder.getMoney()); } catch (GeneralSecurityException e) { e.printStackTrace(); } return R.ok(); } // 假设接收到的请求参数为Map userInfo public PzcUser updateUser(Map userInfo, PzcUser user) { // 反射动态更新用户信息 try { Class clazz = user.getClass(); for (Map.Entry entry : userInfo.entrySet()) { String fieldName = entry.getKey(); Object fieldValue = entry.getValue(); log.info("fieldName is {} , fieldValue is {}", fieldName, fieldValue); if (fieldValue != null) { wxUtils.checkMgc(String .valueOf(fieldValue)); //敏感词检测 } if (fieldValue instanceof Map) //跳过map类型 { continue; } if (fieldValue != null) { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(user, fieldValue); } } } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException("更新用户信息失败 反射异常"); } // 保存更新后的用户信息 userMapper.updateById(user); return userMapper.selectById(user.getUserId()); } public String userLogin(PzcUserBo pzcUserBo) { String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + secret + "&js_code=" + pzcUserBo.getLoginCode() + "&grant_type=authorization_code"; String response = HttpUtil.get(url); log.info("微信小程序登录 url : {},response is {}", url, response); JSONObject wxUser = JSONObject.parseObject(response); if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get("errcode") != null) { throw new RuntimeException("微信登录失败 可能是code过期了"); } String openId = wxUser.get("openid").toString(); //如果存在 就直接返回 不存在就新建用户 PzcUser user = userMapper.selectOne(new QueryWrapper().eq("openid", openId)); if (user == null) { //存入用户信息 PzcUser newUser = new PzcUser(); newUser.setNickname(pzcUserBo.getNickname()); newUser.setOpenid(openId); newUser.setAvatar(pzcUserBo.getAvatar()); //新注册时 根据 POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN 获取手机号 String getPhoneUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + wxUtils.getAccessToken(); Map codeMap = new HashMap<>(); codeMap.put("code", pzcUserBo.getPhoneCode()); String phoneResponse = HttpUtil.post(getPhoneUrl, JSONUtil.toJsonStr(codeMap)); log.info("微信小程序获取用户手机号信息 url : {},response is {}", getPhoneUrl, phoneResponse); cn.hutool.json.JSONObject phoneJson = JSONUtil.parseObj(phoneResponse); if (phoneJson.getInt("errcode") != 0) { log.info("微信小程序获取用户手机号信息失败"); throw new RuntimeException("微信小程序获取用户手机号信息失败"); } newUser.setPhone(phoneJson.getJSONObject("phone_info").getStr("purePhoneNumber")); newUser.setSex(pzcUserBo.getSex()); newUser.setMoney(new BigDecimal(100)); //新用户注册送1元 newUser.setUserLevel(1L); int insert = userMapper.insert(newUser); log.info("insertUser: " + insert); user = userMapper.selectOne(new QueryWrapper().eq("openid", openId)); } if (user.getState() == 0) { throw new RuntimeException("用户已被禁用"); } // 此处可根据登录用户的数据不同 自行创建 loginUser XcxLoginUser loginUser = new XcxLoginUser(); loginUser.setUserId(Long.valueOf(user.getUserId())); loginUser.setUsername(user.getNickname()); loginUser.setUserType("微信小程序用户"); loginUser.setOpenid(openId); // 生成token LoginHelper.loginByDevice(loginUser, DeviceType.XCX); recordLogininfor(user.getNickname(), MessageUtils.message("user.login.success")); return StpUtil.getTokenValue(); } private void recordLogininfor(String username, String message) { LogininforEvent logininforEvent = new LogininforEvent(); logininforEvent.setUsername(username); logininforEvent.setStatus(Constants.LOGIN_SUCCESS); logininforEvent.setMessage(message); logininforEvent.setRequest(ServletUtils.getRequest()); SpringUtils.context().publishEvent(logininforEvent); } @PostMapping("/sendArriveMsg") //推送微信小程序通知 public R sendArriveMsg(String toUserOpenId, String data) { String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?" + "grant_type=client_credential&appid=" + appId + "&secret=" + secret; String response = HttpUtil.get(getTokenUrl); log.info("微信小程序获取token url : {},response is {}", getTokenUrl, response); JSONObject wxUser = JSONObject.parseObject(response); if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get("errcode") != null) { throw new RuntimeException("微信登录失败 可能是code过期了"); } String accessToken = wxUser.get("access_token").toString(); String msgUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken; Map map = new HashMap<>(); map.put("template_id", "MMHCiz9Z5faTwbDI9ywE0ScIvGMeDduTxXm00wdLxmw"); map.put("touser", toUserOpenId); map.put("data", data); map.put("miniprogram_state", "trial");//developer为开发版;trial为体验版;formal为正式版;默认为正式版 map.put("lang", "zh_CN"); String msgResponse = HttpUtil.post(msgUrl, JSONUtil.toJsonStr(map)); log.info("微信小程序推送消息 url : {},response is {}", msgUrl, msgResponse); JSONObject msgJson = JSONObject.parseObject(msgResponse); if (msgJson.getInteger("errcode") != 0) { throw new RuntimeException("微信小程序推送消息失败"); } return R.ok(msgJson.get("errcode").toString()); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivity.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 活动对象 pzc_activity * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_activity") public class PzcActivity extends BaseEntity { private static final long serialVersionUID=1L; /** * 活动id */ @TableId(value = "activity_id", type = IdType.AUTO) private Integer activityId; /** * 地址 */ private String address; /** * 城市ID */ private Integer regionId; /** * 活动标题 */ private String title; /** * 开始时间 */ private String startTime; /** * 结束时间 */ private String endDate; /** * 活动详情主图 */ private String innerImage; /** * 展示时间 */ private String showTime; /** * 封面图片 */ private String coverImage; /** * 删除状态,默认为1表示正常状态 */ private Integer state; private Integer classify; //属于哪个分类 0 电音节 1派对 private Integer region; // 0 国内 1 国外 private Long organizerId; //主办方id private String shareImage; //分享图片 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityConnArtist.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 活动关联艺人对象 pzc_activity_conn_artist * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_activity_conn_artist") public class PzcActivityConnArtist extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "activity_conn_artist_id", type = IdType.AUTO) private Integer activityConnArtistId; /** * 活动ID */ private Integer activityId; /** * 艺人ID */ private Integer artistId; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityConnIntro.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 活动介绍与活动关联对象 pzc_activity_conn_intro * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_activity_conn_intro") public class PzcActivityConnIntro extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "activity_conn_intro_id", type = IdType.AUTO) private Integer activityConnIntroId; /** * 活动ID */ private Integer activityId; /** * 活动介绍ID */ private Integer introId; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityConnTag.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 活动标签与活动关联对象 pzc_activity_conn_tag * * @author ruoyi * @date 2023-06-03 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_activity_conn_tag") public class PzcActivityConnTag extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "activity_conn_tag_id") private Integer activityConnTagId; /** * 活动ID */ private Integer activityId; /** * 活动标签ID */ private Integer tagId; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityGroup.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import java.math.BigDecimal; import java.util.Date; /** * 活动组队对象 pzc_activity_group * * @author ruoyi * @date 2023-07-10 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_activity_group") public class PzcActivityGroup extends BaseEntity { private static final long serialVersionUID=1L; /** * 组队ID */ @TableId(value = "group_id",type = IdType.AUTO) private Long groupId; /** * 活动ID */ private Long activityId; private Integer region; /** * 活动组队发起人ID */ private Long userId; /** * 活动主题 */ private String title; /** * 活动组队所缴纳的保证金 */ private BigDecimal money; /** * 买单方式 */ private Long groupType; /** * 活动地址 */ private String address; /** * 一起约定好的时间 */ private Date activityTime; /** * 权限 */ private Long auth; @TableField(exist = false) private PzcUser user; private Integer status; private String activityName; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcActivityGroupApply.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import java.math.BigDecimal; /** * 活动组队申请列对象 pzc_activity_group_apply * * @author ruoyi * @date 2023-07-10 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_activity_group_apply") public class PzcActivityGroupApply extends BaseEntity { private static final long serialVersionUID=1L; /** * 申请ID */ @TableId(value = "apply_id",type = IdType.AUTO) private Long applyId; /** * 申请人ID */ private Long userId; /** * 申请的活动ID */ private Long activityId; /** * 申请加入的组ID */ private Long groupId; /** * 0 AA制 1 我买单 2 你买单 */ private Long groupType; /** * 活动保证金 */ private BigDecimal money; /** * 留言内容 */ private String message; /** * 0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束 */ private Integer applyStatus; /** * 无限制确认到达 0 未确认 1 已确认 */ private Integer wxz; @TableField(exist = false) private BigDecimal otherMoney; //对方的保证金 @TableField(exist = false) private String otherName; //对方的名字 @TableField(exist = false) private String otherAvatar; //对方的头像 @TableField(exist = false) private String otherUserId; //对方的userId @TableField(exist = false) private Integer otherLevel; //对方的等级 @TableField(exist = false) private String myAvatar; //我的头像 @TableField(exist = false) private String title; //发起方当前位置 private String startAddress; //申请方当前位置 private String applyAddress; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcArtist.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 艺人对象 pzc_artist * * @author flya * @date 2023-06-01 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_artist") public class PzcArtist extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "artist_id", type = IdType.AUTO) private Long artistId; /** * 艺人名 */ private String name; /** * 艺人照片 */ private String imageUrl; /** * 艺人介绍 */ private String description; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcIntro.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 活动介绍对象 pzc_intro * * @author ruoyi * @date 2023-08-04 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_intro") public class PzcIntro extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "intro_id",type = IdType.AUTO) private Long introId; /** * 标题 */ private String title; /** * 内容 */ private String content; /** * 0 场地舞台介绍 1 更多介绍 */ private Long type; /** * 图片 */ private String imageFullUrl; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOfficial.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 官方消息对象 pzc_official * * @author ruoyi * @date 2023-07-27 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_official") public class PzcOfficial extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "official_id",type = IdType.AUTO) private Long officialId; /** * 来自谁的消息 */ private Long fromUserId; /** * 给谁发的消息 */ private Long toUserId; /** * 标题 */ private String title; /** * 主体消息 */ private String content; /** * 是否已读 */ private Long isRead; /** * 来自那场组队的消息 */ private Long groupId; /** * 来自那场活动的消息 */ private Long activityId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOrder.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import java.math.BigDecimal; /** * 订单对象 pzc_order * * @author ruoyi * @date 2023-07-09 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_order") public class PzcOrder extends BaseEntity { private static final long serialVersionUID=1L; /** * 订单ID */ @TableId(value = "order_id",type = IdType.AUTO) private Long orderId; /** * 用户ID */ private Long userId; /** * 活动ID */ private Long activityId; /** * 订单金额 */ private BigDecimal money; /** * 订单状态 */ private Long orderStatus; /** * 订单类型 */ private Long type; /** * 外部订单号 */ private String outOrderNum; /** * 描述 */ private String intro; /** * 标题 */ private String title; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOrganizer.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import java.util.List; /** * 活动主办方对象 pzc_organizer * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_organizer") public class PzcOrganizer extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "organizer_id", type = IdType.AUTO) private Long organizerId; /** * 电话号码 */ private String phone; /** * 名称 */ private String name; /** * 组织者标志图片 */ private String logo; /** * 主办方介绍 */ private String content; /** * 删除状态,默认为1表示正常状态 */ private Integer state; @TableField(exist = false) private List organizerTickets; // 组织者票务列表 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcOrganizerTicket.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 主办方票务对象 pzc_organizer_ticket * * @author ruoyi * @date 2023-07-22 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_organizer_ticket") public class PzcOrganizerTicket extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "organizer_ticket_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Long organizerTicketId; /** * 名称 */ private String name; /** * 二维码图片 */ private String qrImage; /** * logo图 */ private String logoImage; /** * 关联主办方ID */ private Long organizerId; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcRegion.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 地区对象 pzc_region * * @author ruoyi * @date 2023-07-22 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_region") public class PzcRegion extends BaseEntity { private static final long serialVersionUID=1L; /** * 地区id */ @TableId(value = "region_id",type = IdType.AUTO) private Long regionId; /** * 省 */ private String base; /** * 地区名称 */ private String name; /** * 城市主活动图 */ private String imgUrl; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcTag.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 活动标签对象 pzc_tag * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_tag") public class PzcTag extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "tag_id", type = IdType.AUTO) private Long tagId; /** * 名称 */ private String name; /** * 图片 */ private String imageUrl; /** * 删除状态,默认为1表示正常状态 */ private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUser.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.system.domain.vo.PzcActivityGroupApplyVo; import java.math.BigDecimal; import java.util.List; /** * 用户对象 pzc_user * * @author ruoyi * @date 2023-07-09 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_user") public class PzcUser extends BaseEntity { private static final long serialVersionUID=1L; /** * 用户主键 */ @TableId(value = "user_id",type = IdType.AUTO) private Long userId; /** * 用户在小程序端的 openId 唯一 */ private String openid; /** * 派币余额 */ private BigDecimal money; /** * 用户等级 */ private Long userLevel; /** * 用户累计积分 */ private Long integration; /** * 用户现有积分 */ private Long integrationNow; /** * 真实姓名 */ private String realname; /** * 昵称 */ private String nickname; /** * 用户性别 0 男 1 女 2 未知 */ private Integer sex; /** * 手机号 */ private String phone; /** * 头像 */ private String avatar; /** * 地址 */ private String address; /** * 个人介绍 */ private String intro; /** * 年龄 */ private Long age; /** * 星座 */ private String constellation; /** * 人格类型 */ private String mbti; /** * 兴趣爱好 */ private String hobby; /** * 学校 */ private String school; /** * 职业 */ private String occupation; /** * 喜欢的音乐风格 */ private String musicStyle; /** * 状态 是否被封禁 */ private Long state; @TableField(exist = false) private List userPhoto; @TableField(exist = false) private PzcActivityGroup pzcActivityGroup; @TableField(exist = false) private PzcActivityGroupApplyVo pzcActivityGroupApplyVo; /** * 用户免责取消次数 0点定时任务刷新 */ private Integer exemptCancel; @TableField(exist = false) private Boolean liveStatus; @TableField(exist = false) private Integer notReadCount; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserCollect.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 用户收藏活动对象 pzc_user_collect * * @author ruoyi * @date 2023-07-08 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_user_collect") public class PzcUserCollect extends BaseEntity { private static final long serialVersionUID=1L; /** * ID */ @TableId(value = "collect_id",type = IdType.AUTO) private Long collectId; /** * 用户Id */ private Long userId; /** * 收藏活动的Id */ private Long activityId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserHistory.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import java.math.BigDecimal; /** * 用户操作历史记录对象 pzc_user_history * * @author ruoyi * @date 2023-07-06 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_user_history") public class PzcUserHistory extends BaseEntity { private static final long serialVersionUID=1L; /** * */ @TableId(value = "history_id",type = IdType.AUTO) private Long historyId; /** * 关联用户Id */ private Long userId; /** * 关联活动Id */ private Long activityId; /** * 操作类型 */ private Long type; /** * 信息 */ private String message; private BigDecimal money; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserPhoto.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 用户资料相册对象 pzc_user_photo * * @author ruoyi * @date 2023-07-11 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_user_photo") public class PzcUserPhoto extends BaseEntity { private static final long serialVersionUID=1L; /** * 照片ID */ @TableId(value = "photo_id", type = IdType.AUTO) private Long photoId; /** * 用户ID */ private Long userId; /** * 照片 */ private String url; /** * 照片说明 */ private String message; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcUserTalk.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; /** * 用户聊天对象 pzc_user_talk * * @author ruoyi * @date 2023-07-16 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_user_talk") public class PzcUserTalk extends BaseEntity { private static final long serialVersionUID=1L; /** * 聊天ID */ @TableId(value = "talk_id",type = IdType.AUTO) private Long talkId; /** * 发起方 */ private Long fromUserId; /** * 接受方 */ private Long toUserId; private Long userId; //消息的归属者 单向删除 /** * 消息 */ private String message; /** * 消息状态 */ private Long messageStatus; /** * 消息类型 */ private Long messageType; @TableField(exist = false) private Integer notReadCount; private Long groupId; //群组id } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/PzcViewPager.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; /** * 轮播图对象 pzc_view_pager * * @author ruoyi * @date 2023-05-23 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_view_pager") public class PzcViewPager extends BaseEntity { private static final long serialVersionUID=1L; /** * 轮播图id */ @TableId(value = "view_pager_id", type = IdType.AUTO) private Integer viewPagerId; /** * 轮播图名称 */ private String name; /** * 轮播图图片Url */ private String imageUrl; /** * 轮播图链接Url */ private String linkUrl; /** * 删除状态,默认为1表示正常状态 */ private Integer state; /** * 关联活动id 0表示不关联 */ private Long activityId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PayOrderBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import javax.validation.constraints.NotNull; @Data public class PayOrderBo { @NotNull private Integer count; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.system.domain.PzcArtist; import top.flya.system.domain.PzcIntro; import top.flya.system.domain.PzcOrganizer; import top.flya.system.domain.PzcTag; import javax.validation.constraints.*; import java.util.List; /** * 活动业务对象 pzc_activity * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcActivityBo extends BaseEntity { /** * 活动id */ @NotNull(message = "活动id不能为空", groups = { EditGroup.class }) private Integer activityId; /** * 地址 */ @NotBlank(message = "地址不能为空", groups = { AddGroup.class, EditGroup.class }) private String address; /** * 城市ID */ @NotNull(message = "城市ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer regionId; /** * 活动标题 */ @NotBlank(message = "活动标题不能为空", groups = { AddGroup.class, EditGroup.class }) private String title; /** * 开始时间 */ @NotBlank(message = "开始时间不能为空", groups = { AddGroup.class, EditGroup.class }) private String startTime; /** * 结束时间 */ @NotBlank(message = "结束时间不能为空", groups = { AddGroup.class, EditGroup.class }) private String endDate; /** * 展示时间 */ // @NotBlank(message = "展示时间不能为空", groups = { AddGroup.class, EditGroup.class }) private String showTime; /** * 封面图片 */ @NotBlank(message = "封面图片不能为空", groups = { AddGroup.class, EditGroup.class }) private String coverImage; /** * 活动详情主图 */ @NotNull(message = "活动详情主图不能为空", groups = { AddGroup.class, EditGroup.class }) private String innerImage; /** * 删除状态,默认为1表示正常状态 */ private Integer state; private Integer classify; //属于哪个分类 private Integer region; // 0 国际 1 国内 private List stageList; // 场地舞台列表 private List introList; // 简介列表 private List tagList; // 标签列表 private List artistList; // 艺人列表 @NotNull(message = "主办方不能为空", groups = { AddGroup.class, EditGroup.class }) private PzcOrganizer organizerList; // 主办方列表 private String shareImage; //分享图片 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityConnArtistBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 活动关联艺人业务对象 pzc_activity_conn_artist * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcActivityConnArtistBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Integer activityConnArtistId; /** * 活动ID */ @NotNull(message = "活动ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer activityId; /** * 艺人ID */ @NotNull(message = "艺人ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer artistId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityConnIntroBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 活动介绍与活动关联业务对象 pzc_activity_conn_intro * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcActivityConnIntroBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Integer activityConnIntroId; /** * 活动ID */ @NotNull(message = "活动ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer activityId; /** * 活动介绍ID */ @NotNull(message = "活动介绍ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer introId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityConnTagBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 活动标签与活动关联业务对象 pzc_activity_conn_tag * * @author ruoyi * @date 2023-06-03 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcActivityConnTagBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Integer activityConnTagId; /** * 活动ID */ @NotNull(message = "活动ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer activityId; /** * 活动标签ID */ @NotNull(message = "活动标签ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer tagId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityGroupApplyBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; import java.math.BigDecimal; /** * 活动组队申请列业务对象 pzc_activity_group_apply * * @author ruoyi * @date 2023-07-10 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcActivityGroupApplyBo extends BaseEntity { /** * 申请ID */ @NotNull(message = "申请ID不能为空", groups = { EditGroup.class }) private Long applyId; /** * 申请人ID */ // @NotNull(message = "申请人ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long userId; /** * 申请的活动ID */ @NotNull(message = "申请的活动ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long activityId; /** * 申请加入的组ID */ @NotNull(message = "申请加入的组ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long groupId; /** * 申请状态 * 0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束 //申请状态不应该由前端传入 由后端自动计算 */ // private Integer applyStatus; /** * 0 AA制 1 我买单 2 你买单 */ @NotNull(message = "0 AA制 我买单 2 你买单不能为空", groups = { AddGroup.class, EditGroup.class }) private Long groupType; /** * 活动保证金 */ @NotNull(message = "活动保证金不能为空", groups = { AddGroup.class, EditGroup.class }) private BigDecimal money; /** * 留言内容 */ @NotBlank(message = "留言内容不能为空", groups = { AddGroup.class, EditGroup.class }) private String message; // 无限制确认到达 private Integer wxz; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcActivityGroupBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.Date; /** * 活动组队业务对象 pzc_activity_group * * @author ruoyi * @date 2023-07-10 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcActivityGroupBo extends BaseEntity { /** * 组队ID */ @NotNull(message = "组队ID不能为空", groups = { EditGroup.class }) private Long groupId; /** * 活动ID */ @NotNull(message = "活动ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long activityId; private Integer region; /** * 活动组队发起人ID */ private Long userId; private String activityName; /** * 活动主题 */ @NotBlank(message = "活动主题不能为空", groups = { AddGroup.class, EditGroup.class }) private String title; /** * 活动组队所缴纳的保证金 */ @NotNull(message = "活动组队所缴纳的保证金不能为空", groups = { AddGroup.class, EditGroup.class }) private BigDecimal money; /** * 买单方式 */ @NotNull(message = "买单方式不能为空", groups = { AddGroup.class, EditGroup.class }) private Long groupType; /** * 活动地址 */ @NotBlank(message = "活动地址不能为空", groups = { AddGroup.class, EditGroup.class }) private String address; /** * 一起约定好的时间 */ @NotNull(message = "一起约定好的时间不能为空", groups = { AddGroup.class, EditGroup.class }) private Date activityTime; /** * 权限 */ @NotNull(message = "权限不能为空", groups = { AddGroup.class, EditGroup.class }) private Long auth; private Integer userSex; private Integer distance;// 1 private Integer userLevel; private Integer activityTime1; private String longitudeAndLatitude; //经纬度 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcArtistBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 艺人业务对象 pzc_artist * * @author flya * @date 2023-06-01 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcArtistBo extends BaseEntity { /** * ID */ private Long artistId; /** * 艺人名 */ @NotBlank(message = "艺人名不能为空", groups = { AddGroup.class, EditGroup.class }) private String name; /** * 艺人照片 */ private String imageUrl; /** * 艺人介绍 */ private String description; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcIntroBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * 活动介绍业务对象 pzc_intro * * @author ruoyi * @date 2023-08-04 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcIntroBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Long introId; /** * 标题 */ @NotBlank(message = "标题不能为空", groups = { AddGroup.class, EditGroup.class }) private String title; /** * 内容 */ // @NotBlank(message = "内容不能为空", groups = { AddGroup.class, EditGroup.class }) private String content; /** * 0 场地舞台介绍 1 更多介绍 */ @NotNull(message = "0 场地舞台介绍 1 更多介绍不能为空", groups = { AddGroup.class, EditGroup.class }) private Long type; /** * 图片 */ @NotBlank(message = "图片不能为空", groups = { AddGroup.class, EditGroup.class }) private String imageFullUrl; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOfficialBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * 官方消息业务对象 pzc_official * * @author ruoyi * @date 2023-07-27 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcOfficialBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Long officialId; /** * 来自谁的消息 */ @NotNull(message = "来自谁的消息不能为空", groups = { AddGroup.class, EditGroup.class }) private Long fromUserId; /** * 给谁发的消息 */ @NotNull(message = "给谁发的消息不能为空", groups = { AddGroup.class, EditGroup.class }) private Long toUserId; /** * 标题 */ @NotBlank(message = "标题不能为空", groups = { AddGroup.class, EditGroup.class }) private String title; /** * 主体消息 */ @NotBlank(message = "主体消息不能为空", groups = { AddGroup.class, EditGroup.class }) private String content; /** * 是否已读 */ @NotNull(message = "是否已读不能为空", groups = { AddGroup.class, EditGroup.class }) private Long isRead; /** * 来自那场组队的消息 */ @NotNull(message = "来自那场组队的消息不能为空", groups = { AddGroup.class, EditGroup.class }) private Long groupId; /** * 来自那场活动的消息 */ @NotNull(message = "来自那场活动的消息不能为空", groups = { AddGroup.class, EditGroup.class }) private Long activityId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOrderBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; import java.math.BigDecimal; /** * 订单业务对象 pzc_order * * @author ruoyi * @date 2023-07-09 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcOrderBo extends BaseEntity { /** * 订单ID */ @NotNull(message = "订单ID不能为空", groups = { EditGroup.class }) private Long orderId; /** * 用户ID */ @NotNull(message = "用户ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long userId; /** * 活动ID */ @NotNull(message = "活动ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long activityId; /** * 订单金额 */ @NotNull(message = "订单金额不能为空", groups = { AddGroup.class, EditGroup.class }) private BigDecimal money; /** * 订单状态 */ @NotNull(message = "订单状态不能为空", groups = { AddGroup.class, EditGroup.class }) private Long orderStatus; /** * 订单类型 */ @NotNull(message = "订单类型不能为空", groups = { AddGroup.class, EditGroup.class }) private Long type; /** * 外部订单号 */ @NotBlank(message = "外部订单号不能为空", groups = { AddGroup.class, EditGroup.class }) private String outOrderNum; /** * 描述 */ @NotBlank(message = "描述不能为空", groups = { AddGroup.class, EditGroup.class }) private String intro; /** * 标题 */ @NotBlank(message = "标题不能为空", groups = { AddGroup.class, EditGroup.class }) private String title; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOrganizerBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 活动主办方业务对象 pzc_organizer * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcOrganizerBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Long organizerId; /** * 电话号码 */ @NotBlank(message = "电话号码不能为空", groups = { AddGroup.class, EditGroup.class }) private String phone; /** * 名称 */ @NotBlank(message = "名称不能为空", groups = { AddGroup.class, EditGroup.class }) private String name; /** * 组织者标志图片 */ private String logo; /** * 主办方介绍 */ @NotBlank(message = "主办方介绍不能为空", groups = { AddGroup.class, EditGroup.class }) private String content; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcOrganizerTicketBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * 主办方票务业务对象 pzc_organizer_ticket * * @author ruoyi * @date 2023-07-22 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcOrganizerTicketBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Long organizerTicketId; /** * 名称 */ @NotBlank(message = "名称不能为空", groups = { AddGroup.class, EditGroup.class }) private String name; /** * 二维码图片 */ @NotBlank(message = "二维码图片不能为空", groups = { AddGroup.class, EditGroup.class }) private String qrImage; /** * logo图 */ @NotBlank(message = "logo图不能为空", groups = { AddGroup.class, EditGroup.class }) private String logoImage; /** * 关联主办方ID */ @NotNull(message = "关联主办方ID不能为空", groups = { AddGroup.class, EditGroup.class }) private Long organizerId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcRegionBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * 地区业务对象 pzc_region * * @author ruoyi * @date 2023-07-22 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcRegionBo extends BaseEntity { /** * 地区id */ @NotNull(message = "地区id不能为空", groups = { EditGroup.class }) private Long regionId; /** * 省 */ @NotBlank(message = "省不能为空", groups = { AddGroup.class, EditGroup.class }) private String base; /** * 地区名称 */ @NotBlank(message = "地区名称不能为空", groups = { AddGroup.class, EditGroup.class }) private String name; /** * 城市主活动图 */ @NotBlank(message = "城市主活动图不能为空", groups = { AddGroup.class, EditGroup.class }) private String imgUrl; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcTagBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 活动标签业务对象 pzc_tag * * @author ruoyi * @date 2023-06-02 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcTagBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Long tagId; /** * 名称 */ @NotBlank(message = "名称不能为空", groups = { AddGroup.class, EditGroup.class }) private String name; /** * 图片 */ @NotBlank(message = "图片不能为空", groups = { AddGroup.class, EditGroup.class }) private String imageUrl; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; import java.math.BigDecimal; /** * 用户业务对象 pzc_user * * @author ruoyi * @date 2023-07-06 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcUserBo extends BaseEntity { /** * 用户主键 */ @NotNull(message = "用户主键不能为空", groups = { EditGroup.class }) private Long userId; /** * OpenId */ private String openid; /** * 换取openid的code */ private String loginCode; /** * 换取手机号的code */ private String phoneCode; /** * 派币余额 */ private BigDecimal money; /** * 真实姓名 */ @NotBlank(message = "真实姓名不能为空", groups = { AddGroup.class, EditGroup.class }) private String realname; /** * 昵称 */ @NotBlank(message = "昵称不能为空", groups = { AddGroup.class, EditGroup.class }) private String nickname; /** * 性别 */ @NotNull(message = "性别不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer sex; /** * 用户等级 */ private Long userLevel; /** * 用户累计积分 */ private Long integration; /** * 用户现有积分 */ private Long integrationNow; /** * 手机号 */ private String phone; /** * 头像 */ private String avatar; /** * 地址 */ private String address; /** * 介绍 */ private String intro; /** * 年龄 */ private Long age; /** * 星座 */ private String constellation; /** * 人格类型 */ private String mbti; /** * 兴趣爱好 */ private String hobby; /** * 学校 */ private String school; /** * 职业 */ private String occupation; /** * 音乐风格 */ private String musicStyle; /** * 封禁状态 */ private Integer state; private Integer exemptCancel; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserCollectBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 用户收藏活动业务对象 pzc_user_collect * * @author ruoyi * @date 2023-07-08 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcUserCollectBo extends BaseEntity { /** * ID */ @NotNull(message = "ID不能为空", groups = { EditGroup.class }) private Long collectId; /** * 用户Id */ @NotNull(message = "用户Id不能为空", groups = { EditGroup.class }) private Long userId; /** * 收藏活动的Id */ @NotNull(message = "收藏活动的Id不能为空", groups = { AddGroup.class, EditGroup.class }) private Long activityId; private Integer type ; // 收藏类型 0电音节 1派对 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserHistoryBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * 用户操作历史记录业务对象 pzc_user_history * * @author ruoyi * @date 2023-07-06 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcUserHistoryBo extends BaseEntity { /** * */ @NotNull(message = "不能为空", groups = { EditGroup.class }) private Long historyId; /** * 关联用户Id */ @NotNull(message = "关联用户Id不能为空", groups = { AddGroup.class, EditGroup.class }) private Long userId; /** * 关联活动Id */ @NotNull(message = "关联活动Id不能为空", groups = { AddGroup.class, EditGroup.class }) private Long activityId; /** * 操作类型 */ @NotNull(message = "操作类型不能为空", groups = { AddGroup.class, EditGroup.class }) private Long[] type; /** * 信息 */ @NotBlank(message = "信息不能为空", groups = { AddGroup.class, EditGroup.class }) private String message; private String nowTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserPhotoBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 用户资料相册业务对象 pzc_user_photo * * @author ruoyi * @date 2023-07-11 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcUserPhotoBo extends BaseEntity { /** * 照片ID */ @NotNull(message = "照片ID不能为空", groups = { EditGroup.class }) private Long photoId; /** * 用户ID */ private Long userId; /** * 照片 */ @NotBlank(message = "照片不能为空", groups = { AddGroup.class, EditGroup.class }) private String url; /** * 照片说明 */ private String message; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcUserTalkBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 用户聊天业务对象 pzc_user_talk * * @author ruoyi * @date 2023-07-16 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcUserTalkBo extends BaseEntity { /** * 聊天ID */ @NotNull(message = "聊天ID不能为空", groups = { EditGroup.class }) private Long talkId; /** * 发起方 */ @NotNull(message = "发起方不能为空", groups = { AddGroup.class, EditGroup.class }) private Long fromUserId; /** * 接受方 */ @NotNull(message = "接受方不能为空", groups = { AddGroup.class, EditGroup.class }) private Long toUserId; /** * 消息 */ @NotBlank(message = "消息不能为空", groups = { AddGroup.class, EditGroup.class }) private String message; /** * 消息状态 */ // @NotNull(message = "消息状态不能为空", groups = { AddGroup.class, EditGroup.class }) private Long messageStatus; /** * 消息类型 */ @NotNull(message = "消息类型不能为空", groups = { AddGroup.class, EditGroup.class }) private Long messageType; private Long userId; //消息的归属者 单向删除 private Long groupId; //群组id } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/PzcViewPagerBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import javax.validation.constraints.*; /** * 轮播图业务对象 pzc_view_pager * * @author ruoyi * @date 2023-05-23 */ @Data @EqualsAndHashCode(callSuper = true) public class PzcViewPagerBo extends BaseEntity { /** * 轮播图id */ @NotNull(message = "轮播图id不能为空", groups = { EditGroup.class }) private Integer viewPagerId; /** * 轮播图名称 */ @NotBlank(message = "轮播图名称不能为空", groups = { AddGroup.class, EditGroup.class }) private String name; /** * 轮播图图片Url */ @NotBlank(message = "轮播图图片Url不能为空", groups = { AddGroup.class, EditGroup.class }) private String imageUrl; /** * 轮播图链接Url */ @NotBlank(message = "轮播图链接Url不能为空", groups = { AddGroup.class, EditGroup.class }) private String linkUrl; /** * 删除状态,默认为1表示正常状态 */ @NotNull(message = "删除状态,默认为1表示正常状态不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer state; /** * 关联活动id 0表示不关联 */ @NotNull(message = "关联活动id 0表示不关联不能为空", groups = { AddGroup.class, EditGroup.class }) private Long activityId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/RefurbishBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; @Data public class RefurbishBo { private Long applyId; private Integer role; private String address; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/Resource.java ================================================ package top.flya.system.domain.bo; import lombok.Data; /** * Created with IntelliJ IDEA. * * @author: 风离 * @Date: 2022/06/04/2:59 * @Description: */ @Data public class Resource { private String original_type; private String algorithm; private String ciphertext; private String associated_data; private String nonce; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/SuccessCallBackObjBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; /** * Created with IntelliJ IDEA. * * @author: 风离 * @Date: 2022/06/04/2:57 * @Description: */ @Data public class SuccessCallBackObjBo { private String id; private String create_time; private String event_type; private String resource_type; private Resource resource; private String summary; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/UpdateMoneyBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; import java.math.BigDecimal; @Data public class UpdateMoneyBo { private Long userId; private BigDecimal money; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/bo/WxzApplyBo.java ================================================ package top.flya.system.domain.bo; import lombok.Data; @Data public class WxzApplyBo { private Integer fromUserId; private Integer toUserId; private Integer activityId; private Integer groupId; private Integer applyId; private String message; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityConnArtistVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.util.Date; /** * 活动关联艺人视图对象 pzc_activity_conn_artist * * @author ruoyi * @date 2023-06-02 */ @Data @ExcelIgnoreUnannotated public class PzcActivityConnArtistVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Integer activityConnArtistId; /** * 活动ID */ @ExcelProperty(value = "活动ID") private Integer activityId; /** * 艺人ID */ @ExcelProperty(value = "艺人ID") private Integer artistId; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; /** * 删除状态,默认为1表示正常状态 */ @ExcelProperty(value = "删除状态,默认为1表示正常状态") private Integer state; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityConnIntroVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.util.Date; /** * 活动介绍与活动关联视图对象 pzc_activity_conn_intro * * @author ruoyi * @date 2023-06-02 */ @Data @ExcelIgnoreUnannotated public class PzcActivityConnIntroVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Integer activityConnIntroId; /** * 活动ID */ @ExcelProperty(value = "活动ID") private Integer activityId; /** * 活动介绍ID */ @ExcelProperty(value = "活动介绍ID") private Integer introId; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityConnTagVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.util.Date; /** * 活动标签与活动关联视图对象 pzc_activity_conn_tag * * @author ruoyi * @date 2023-06-03 */ @Data @ExcelIgnoreUnannotated public class PzcActivityConnTagVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Integer activityConnTagId; /** * 活动ID */ @ExcelProperty(value = "活动ID") private Integer activityId; /** * 活动标签ID */ @ExcelProperty(value = "活动标签ID") private Integer tagId; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityGroupApplyVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import java.math.BigDecimal; import java.util.Date; /** * 活动组队申请列视图对象 pzc_activity_group_apply * * @author ruoyi * @date 2023-07-10 */ @Data @ExcelIgnoreUnannotated public class PzcActivityGroupApplyVo { private static final long serialVersionUID = 1L; /** * 申请ID */ @ExcelProperty(value = "申请ID") private Long applyId; /** * 申请人ID */ @ExcelProperty(value = "申请人ID") private Long userId; /** * 申请的活动ID */ @ExcelProperty(value = "申请的活动ID") private Long activityId; /** * 活动标题 */ private String activityTitle; /** * 组队的标题 */ private String groupTitle; /** * 申请加入的组ID */ @ExcelProperty(value = "申请加入的组ID") private Long groupId; /** * 0 AA制 1 我买单 2 你买单 */ @ExcelProperty(value = "0 AA制 1 我买单 2 你买单", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "group_pay_type") private Long groupType; /** * 活动保证金 */ @ExcelProperty(value = "活动保证金") private BigDecimal money; /** * 留言内容 */ @ExcelProperty(value = "留言内容") private String message; /** * 0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束 */ @ExcelProperty(value = "-1 已取消 0 位于申请列表中 1 申请通过待确认时 2 确认通过进行中 3 组队结束", converter = ExcelDictConvert.class) private Integer applyStatus; /** * */ @ExcelProperty(value = "") private Date createTime; /** * */ @ExcelProperty(value = "") private Date updateTime; //申请人的头像以及用户名 private String nickName; private String avatar; /** * 无限制确认到达 0 未确认 1 已确认 */ private Integer wxz; //发起方当前位置 private String startAddress; //申请方当前位置 private String applyAddress; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityGroupVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import top.flya.system.domain.PzcUserPhoto; import java.math.BigDecimal; import java.util.Date; import java.util.List; /** * 活动组队视图对象 pzc_activity_group * * @author ruoyi * @date 2023-07-10 */ @Data @ExcelIgnoreUnannotated public class PzcActivityGroupVo { private static final long serialVersionUID = 1L; /** * 组队ID */ @ExcelProperty(value = "组队ID") private Long groupId; /** * 活动ID */ @ExcelProperty(value = "活动ID") private Long activityId; private Integer region; /** * 活动组队发起人ID */ @ExcelProperty(value = "活动组队发起人ID") private Long userId; /** * 活动组队发起人 */ private PzcUserVo user; /** * 活动主题 */ @ExcelProperty(value = "活动主题") private String title; private String activityTitle; /** * 活动组队所缴纳的保证金 */ @ExcelProperty(value = "活动组队所缴纳的保证金") private BigDecimal money; /** * 买单方式 */ @ExcelProperty(value = "买单方式", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "group_pay_type") private Long groupType; /** * 活动地址 */ @ExcelProperty(value = "活动地址") private String address; /** * 一起约定好的时间 */ @ExcelProperty(value = "一起约定好的时间") private Date activityTime; /** * 权限 */ @ExcelProperty(value = "权限", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "group_auth") private Long auth; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; private List photo; private Boolean ifShow = true; private BigDecimal distance; //距离多少米 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcActivityVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import top.flya.system.domain.PzcArtist; import top.flya.system.domain.PzcIntro; import top.flya.system.domain.PzcOrganizer; import top.flya.system.domain.PzcTag; import java.util.Date; import java.util.List; /** * 活动视图对象 pzc_activity * * @author ruoyi * @date 2023-06-02 */ @Data @ExcelIgnoreUnannotated @AllArgsConstructor @NoArgsConstructor public class PzcActivityVo { private static final long serialVersionUID = 1L; /** * 活动id */ @ExcelProperty(value = "活动id") private Integer activityId; /** * 地址 */ @ExcelProperty(value = "地址") private String address; /** * 城市ID */ @ExcelProperty(value = "城市ID") private Integer regionId; /** * 活动标题 */ @ExcelProperty(value = "活动标题") private String title; /** * 开始时间 */ @ExcelProperty(value = "开始时间") private String startTime; /** * 结束时间 */ @ExcelProperty(value = "结束时间") private String endDate; /** * 活动详情主图 */ @ExcelProperty(value = "活动详情主图") private String innerImage; /** * 展示时间 */ @ExcelProperty(value = "展示时间") private String showTime; /** * 封面图片 */ @ExcelProperty(value = "封面图片") private String coverImage; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; /** * 删除状态,默认为1表示正常状态 */ @ExcelProperty(value = "删除状态,默认为1表示正常状态") private Integer state; private Long organizerId; // 主办方id private Integer classify; //属于哪个分类 private Integer region; // 0 国内 1 国外 private List introList; // 简介列表 private List tagList; // 标签列表 private List artistList; // 艺人列表 private PzcOrganizer organizerList; // 主办方列表 private List stageList; // 场地列表 private String shareImage; //分享图片 // private String distance; //距我多少km } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcArtistVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; /** * 艺人视图对象 pzc_artist * * @author flya * @date 2023-06-01 */ @Data @ExcelIgnoreUnannotated @AllArgsConstructor @NoArgsConstructor public class PzcArtistVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Long artistId; /** * 艺人名 */ @ExcelProperty(value = "艺人名") private String name; /** * 艺人照片 */ @ExcelProperty(value = "艺人照片") private String imageUrl; /** * 艺人介绍 */ @ExcelProperty(value = "艺人介绍") private String description; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcIntroVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; /** * 活动介绍视图对象 pzc_intro * * @author ruoyi * @date 2023-08-04 */ @Data @ExcelIgnoreUnannotated @AllArgsConstructor @NoArgsConstructor public class PzcIntroVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Long introId; /** * 标题 */ @ExcelProperty(value = "标题") private String title; /** * 内容 */ @ExcelProperty(value = "内容") private String content; /** * 0 场地舞台介绍 1 更多介绍 */ @ExcelProperty(value = "0 场地舞台介绍 1 更多介绍") private Long type; /** * 图片 */ @ExcelProperty(value = "图片") private String imageFullUrl; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOfficialVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import java.util.Date; /** * 官方消息视图对象 pzc_official * * @author ruoyi * @date 2023-07-27 */ @Data @ExcelIgnoreUnannotated public class PzcOfficialVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Long officialId; /** * 来自谁的消息 */ @ExcelProperty(value = "来自谁的消息") private Long fromUserId; /** * 给谁发的消息 */ @ExcelProperty(value = "给谁发的消息") private Long toUserId; /** * 标题 */ @ExcelProperty(value = "标题") private String title; /** * 主体消息 */ @ExcelProperty(value = "主体消息") private String content; /** * 是否已读 */ @ExcelProperty(value = "是否已读") private Long isRead; /** * 来自那场组队的消息 */ @ExcelProperty(value = "来自那场组队的消息") private Long groupId; /** * 来自那场活动的消息 */ @ExcelProperty(value = "来自那场活动的消息") private Long activityId; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOrderVo.java ================================================ package top.flya.system.domain.vo; import java.math.BigDecimal; import java.util.Date; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; /** * 订单视图对象 pzc_order * * @author ruoyi * @date 2023-07-09 */ @Data @ExcelIgnoreUnannotated public class PzcOrderVo { private static final long serialVersionUID = 1L; /** * 订单ID */ @ExcelProperty(value = "订单ID") private Long orderId; /** * 用户ID */ @ExcelProperty(value = "用户ID") private Long userId; /** * 活动ID */ @ExcelProperty(value = "活动ID") private Long activityId; /** * 订单金额 */ @ExcelProperty(value = "订单金额") private BigDecimal money; /** * 订单状态 */ @ExcelProperty(value = "订单状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "order_status") private Long orderStatus; /** * 订单类型 */ @ExcelProperty(value = "订单类型", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "order_type") private Long type; /** * 外部订单号 */ @ExcelProperty(value = "外部订单号") private String outOrderNum; /** * 描述 */ @ExcelProperty(value = "描述") private String intro; /** * 标题 */ @ExcelProperty(value = "标题") private String title; /** * 订单创建时间 */ @ExcelProperty(value = "订单创建时间") private Date createTime; /** * 订单更新时间 */ @ExcelProperty(value = "订单更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOrganizerTicketVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; /** * 主办方票务视图对象 pzc_organizer_ticket * * @author ruoyi * @date 2023-07-22 */ @Data @ExcelIgnoreUnannotated public class PzcOrganizerTicketVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Long organizerTicketId; /** * 名称 */ @ExcelProperty(value = "名称") private String name; /** * 二维码图片 */ @ExcelProperty(value = "二维码图片") private String qrImage; /** * logo图 */ @ExcelProperty(value = "logo图") private String logoImage; /** * 关联主办方ID */ @ExcelProperty(value = "关联主办方ID") private Long organizerId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcOrganizerVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.util.Date; /** * 活动主办方视图对象 pzc_organizer * * @author ruoyi * @date 2023-06-02 */ @Data @ExcelIgnoreUnannotated @AllArgsConstructor @NoArgsConstructor public class PzcOrganizerVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Long organizerId; /** * 电话号码 */ @ExcelProperty(value = "电话号码") private String phone; /** * 名称 */ @ExcelProperty(value = "名称") private String name; /** * 组织者标志图片 */ @ExcelProperty(value = "组织者标志图片") private String logo; /** * 主办方介绍 */ @ExcelProperty(value = "主办方介绍") private String content; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcRegionVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Data; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 地区视图对象 pzc_region * * @author ruoyi * @date 2023-07-22 */ @Data @ExcelIgnoreUnannotated @AllArgsConstructor public class PzcRegionVo { private static final long serialVersionUID = 1L; /** * 地区id */ @ExcelProperty(value = "地区id") private Long regionId; /** * 省 */ @ExcelProperty(value = "省") private String base; /** * 地区名称 */ @ExcelProperty(value = "地区名称") private String name; /** * 城市主活动图 */ @ExcelProperty(value = "城市主活动图") private String imgUrl; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; /** * 子地区列表 */ private List children; // 构造方法 public PzcRegionVo() { this.children = new ArrayList<>(); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcTagVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.util.Date; /** * 活动标签视图对象 pzc_tag * * @author ruoyi * @date 2023-06-02 */ @Data @ExcelIgnoreUnannotated @AllArgsConstructor @NoArgsConstructor public class PzcTagVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Long tagId; /** * 名称 */ @ExcelProperty(value = "名称") private String name; /** * 图片 */ @ExcelProperty(value = "图片") private String imageUrl; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserCollectVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import top.flya.system.domain.PzcActivity; /** * 用户收藏活动视图对象 pzc_user_collect * * @author ruoyi * @date 2023-07-08 */ @Data @ExcelIgnoreUnannotated public class PzcUserCollectVo { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "ID") private Long collectId; /** * 用户Id */ @ExcelProperty(value = "用户Id") private Long userId; /** * 收藏活动的Id */ @ExcelProperty(value = "收藏活动的Id") private Long activityId; private PzcActivity activity; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserHistoryVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.math.BigDecimal; import java.util.Date; /** * 用户操作历史记录视图对象 pzc_user_history * * @author ruoyi * @date 2023-07-06 */ @Data @ExcelIgnoreUnannotated public class PzcUserHistoryVo { private static final long serialVersionUID = 1L; /** * */ @ExcelProperty(value = "") private Long historyId; /** * 关联用户Id */ @ExcelProperty(value = "关联用户Id") private Long userId; /** * 关联活动Id */ @ExcelProperty(value = "关联活动Id") private Long activityId; /** * 操作类型 */ @ExcelProperty(value = "操作类型", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "user_history_type") private Long type; /** * 信息 */ @ExcelProperty(value = "信息") private String message; private BigDecimal money; private Date createTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserPhotoVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.util.Date; /** * 用户资料相册视图对象 pzc_user_photo * * @author ruoyi * @date 2023-07-11 */ @Data @ExcelIgnoreUnannotated public class PzcUserPhotoVo { private static final long serialVersionUID = 1L; /** * 照片ID */ @ExcelProperty(value = "照片ID") private Long photoId; /** * 用户ID */ @ExcelProperty(value = "用户ID") private Long userId; /** * 照片 */ @ExcelProperty(value = "照片") private String url; /** * 照片说明 */ @ExcelProperty(value = "照片说明") private String message; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ @ExcelProperty(value = "更新时间") private Date updateTime; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserTalkVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.util.Date; /** * 用户聊天视图对象 pzc_user_talk * * @author ruoyi * @date 2023-07-16 */ @Data @ExcelIgnoreUnannotated public class PzcUserTalkVo { private static final long serialVersionUID = 1L; /** * 聊天ID */ @ExcelProperty(value = "聊天ID") private Long talkId; /** * 发起方 */ @ExcelProperty(value = "发起方") private Long fromUserId; /** * 接受方 */ @ExcelProperty(value = "接受方") private Long toUserId; /** * 消息 */ @ExcelProperty(value = "消息", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "user_talk_msg_type") private String message; /** * 消息状态 */ @ExcelProperty(value = "消息状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "user_talk_msg_status") private Long messageStatus; /** * 消息类型 */ @ExcelProperty(value = "消息类型") private Long messageType; /** * */ @ExcelProperty(value = "") private Date createTime; /** * */ @ExcelProperty(value = "") private Date updateTime; private Integer notReadCount; //未读消息数量 private String username; private String avatar; private Long groupId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcUserVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import java.math.BigDecimal; import java.util.Date; /** * 用户视图对象 pzc_user * * @author ruoyi * @date 2023-07-09 */ @Data @AllArgsConstructor @NoArgsConstructor @ExcelIgnoreUnannotated public class PzcUserVo { private static final long serialVersionUID = 1L; /** * 用户主键 */ @ExcelProperty(value = "用户主键") private Long userId; /** * 用户在小程序端的 openId 唯一 */ @ExcelProperty(value = "用户在小程序端的 openId 唯一") private String openid; /** * 派币余额 */ @ExcelProperty(value = "派币余额") private BigDecimal money; /** * 用户等级 */ @ExcelProperty(value = "用户等级", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "user_level") private Long userLevel; /** * 用户累计积分 */ @ExcelProperty(value = "用户累计积分") private Long integration; /** * 用户现有积分 */ @ExcelProperty(value = "用户现有积分") private Long integrationNow; /** * 真实姓名 */ @ExcelProperty(value = "真实姓名") private String realname; /** * 昵称 */ @ExcelProperty(value = "昵称") private String nickname; /** * 用户性别 0 男 1 女 2 未知 */ @ExcelProperty(value = "用户性别 0 男 1 女 2 未知", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_user_sex") private Integer sex; /** * 手机号 */ @ExcelProperty(value = "手机号") private String phone; /** * 头像 */ @ExcelProperty(value = "头像") private String avatar; /** * 地址 */ @ExcelProperty(value = "地址") private String address; /** * 个人介绍 */ @ExcelProperty(value = "个人介绍") private String intro; /** * 年龄 */ @ExcelProperty(value = "年龄") private Long age; /** * 星座 */ @ExcelProperty(value = "星座", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "constellation") private String constellation; /** * 人格类型 */ @ExcelProperty(value = "人格类型", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "mbti") private String mbti; /** * 兴趣爱好 */ @ExcelProperty(value = "兴趣爱好", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "user_hobby") private String hobby; /** * 学校 */ @ExcelProperty(value = "学校") private String school; /** * 职业 */ @ExcelProperty(value = "职业") private String occupation; /** * 创建时间 */ @ExcelProperty(value = "创建时间") private Date createTime; /** * 更新时间 */ private Date updateTime; /** * 喜欢的音乐风格 */ @ExcelProperty(value = "喜欢的音乐风格", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "music_style") private String musicStyle; /** * 状态 是否被封禁 */ @ExcelProperty(value = "状态 是否被封禁", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "state") private Long state; private Integer exemptCancel; // 用户免责取消次数 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/PzcViewPagerVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * 轮播图视图对象 pzc_view_pager * * @author ruoyi * @date 2023-05-23 */ @Data @ExcelIgnoreUnannotated @AllArgsConstructor @NoArgsConstructor public class PzcViewPagerVo { private static final long serialVersionUID = 1L; /** * 轮播图id */ @ExcelProperty(value = "轮播图id") private Integer viewPagerId; /** * 轮播图名称 */ @ExcelProperty(value = "轮播图名称") private String name; /** * 轮播图图片Url */ @ExcelProperty(value = "轮播图图片Url") private String imageUrl; /** * 轮播图链接Url */ @ExcelProperty(value = "轮播图链接Url") private String linkUrl; /** * 删除状态,默认为1表示正常状态 */ @ExcelProperty(value = "删除状态,默认为1表示正常状态") private Integer state; /** * 关联活动id 0表示不关联 */ @ExcelProperty(value = "关联活动id 0表示不关联") private Long activityId; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/domain/vo/RefurbishVO.java ================================================ package top.flya.system.domain.vo; import lombok.Data; @Data public class RefurbishVO { //发起方当前位置 private String startAddress; //申请方当前位置 private String applyAddress; private Long applyId; private Long distance; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Activity.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; @Data @EqualsAndHashCode(callSuper = true) //活动 @TableName("pzc_activity") public class Activity extends FLBaseEntity { @TableId(value = "activity_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private String activityId; //活动id private String address; // 地址 private Integer regionId; // 城市ID private String title; //活动标题 private String startTime; // 开始时间 private String endDate; // 结束日期 private String innerImage; // 活动详情主图 private String showTime; // 展示时间 private String coverImage; // 封面图片 private Integer classify; //属于哪个分类 private Integer region; // 0 国内 1 国外 @TableField(exist = false) private List introList; // 简介列表 @TableField(exist = false) //标签列表 private List tagList; @TableField(exist = false) private List artistList; // 艺人列表 @TableField(exist = false) private Organizer organizerList; // 主办方列表 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/ActivityConnArtist.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) //活动关联艺人 @TableName("pzc_activity_conn_artist") public class ActivityConnArtist extends FLBaseEntity{ @TableId(value = "activity_conn_artist_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer activityConnArtistId; // ID private Integer activityId; // 活动ID private Integer artistId; // 艺人ID } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/ActivityConnIntro.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @TableName("pzc_activity_conn_intro") @EqualsAndHashCode(callSuper = true) //活动介绍 与 活动 关联表 public class ActivityConnIntro extends FLBaseEntity{ @TableId(value = "activity_conn_intro_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer activityConnIntroId; // ID private Integer activityId; // 活动ID private Integer introId; // 活动介绍ID } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/ActivityConnTag.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) //活动标签 @TableName("pzc_activity_conn_tag") public class ActivityConnTag extends FLBaseEntity{ @TableId(value = "activity_conn_tag_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer activityConnTagId; // ID private Integer activityId; // 活动ID private Integer tagId; // 活动标签ID } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Artist.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @TableName("pzc_artist") @EqualsAndHashCode(callSuper = true) //艺人 public class Artist extends FLBaseEntity{ @TableId(value = "artist_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer artistId; // ID private String name; // 名称 private String imageUrl; // 图片 private String description; // 描述 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Event.java ================================================ package top.flya.system.entity; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.util.List; @Data public class Event { @JsonProperty("one_way_exit_permits_flag") private int oneWayExitPermitsFlag; // 单向出口许可标志 @JsonProperty("sale_start_time") private long saleStartTime; // 销售开始时间 @JsonProperty("music_type") private int musicType; // 音乐类型 @JsonProperty("post_flag") private int postFlag; // 发布标志 @JsonProperty("logo") private String logo; // 标志图片 @JsonProperty("id") private int id; // ID @JsonProperty("ticket_type") private int ticketType; // 票务类型 @JsonProperty("end_time") private String endTime; // 结束时间 @JsonProperty("email_flag") private int emailFlag; // 电子邮件标志 @JsonProperty("cover_image") private String coverImage; // 封面图片 @JsonProperty("state") private int state; // 状态 @JsonProperty("telephone") private String telephone; // 电话号码 @JsonProperty("passport_flag") private int passportFlag; // 护照标志 @JsonProperty("address") private String address; // 地址 @JsonProperty("miniapp_code_image") private String miniappCodeImage; // 小程序码图片 @JsonProperty("one_time_notify_flag") private int oneTimeNotifyFlag; // 一次性通知标志 @JsonProperty("recommend_tickets") private List recommendTickets; // 推荐票务 @JsonProperty("star_flag") private int starFlag; // 星标标志 @JsonProperty("merchant_id") private int merchantId; // 商户ID @JsonProperty("notice_list") private List noticeList; // 通知列表 @JsonProperty("artist_list") private List artistList; // 艺术家列表 @JsonProperty("withdraw_state") private int withdrawState; // 提款状态 @JsonProperty("intro_list") private List introList; // 简介列表 @JsonProperty("type") private int type; // 类型 @JsonProperty("music_type_text") private String musicTypeText; // 音乐类型文本 @JsonProperty("ad_flag") private int adFlag; // 广告标志 @JsonProperty("end_date") private String endDate; // 结束日期 @JsonProperty("payment_method") private int paymentMethod; // 支付方式 @JsonProperty("id_card_flag") private int idCardFlag; // 身份证标志 @JsonProperty("payment_state") private int paymentState; // 支付状态 @JsonProperty("offline_payment_flag") private int offlinePaymentFlag; // 线下支付标志 @JsonProperty("name") private String name; // 名称 @JsonProperty("organizer") private Organizer organizer; @JsonProperty("longitude") private String longitude; // 经度 @JsonProperty("price") private List price; // 价格列表 @JsonProperty("start_date") private String startDate; // 开始日期 @JsonProperty("sub_state") private int subState; // 子状态 @JsonProperty("rec_flag") private int recFlag; // 推荐标志 @JsonProperty("start_time") private String startTime; // 开始时间 @JsonProperty("bytedance_code_image") private String bytedanceCodeImage; // 字节跳动码图片 @JsonProperty("city_id") private int cityId; // 城市ID @JsonProperty("sale_end_time") private String innerImage; // 活动详情主图 @JsonProperty("check_code_image") private String checkCodeImage; // 检查码图片 @JsonProperty("show_time") private String showTime; // 展示时间 @JsonProperty("latitude") private String latitude; // 纬度 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/FLBaseEntity.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.Date; @Data @NoArgsConstructor public class FLBaseEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 创建时间 */ @TableField(fill = FieldFill.INSERT) private Date createTime; /** * 更新时间 */ @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; /** * 状态 */ private Integer state ; // 状态 默认正常状态 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Intro.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; /** * 活动介绍 设置 多对多 关联 方便 以后扩展 */ @Data @EqualsAndHashCode(callSuper = true) //活动介绍 @TableName("pzc_intro") public class Intro extends FLBaseEntity{ @TableId(value = "intro_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer introId; // ID private String content; // 内容 private String imageFullUrl; // 完整图片URL } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Organizer.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; @Data @EqualsAndHashCode(callSuper = true) //活动主办方 @TableName("pzc_organizer") public class Organizer extends FLBaseEntity{ //活动主办方 @TableId(value = "organizer_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer organizerId; // ID private String phone; // 电话号码 private String name; // 名称 private String logo; // 组织者标志图片 private String content; //主办方介绍 @TableField(exist = false) private List organizerTickets; // 组织者票务列表 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/OrganizerTicket.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) //主办方票务 @TableName("pzc_organizer_ticket") public class OrganizerTicket extends FLBaseEntity{ // 主办方票务 @TableId(value = "organizer_ticket_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private int organizerTicketId; // ID private String name; // 名称 private String startDate; // 开始日期 private String coverImage; // 封面图片 private Integer organizerId; // 关联主办方ID } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Price.java ================================================ package top.flya.system.entity; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.util.List; @Data public class Price { @JsonProperty("stock_num") private int stockNum; // 库存数量 @JsonProperty("ticket_id") private int ticketId; // 票务ID @JsonProperty("id") private int id; // ID @JsonProperty("date_name") private String dateName; // 日期名称 @JsonProperty("price") private String price; // 价格 @JsonProperty("date") private List date; // 日期列表 @JsonProperty("limit") private int limit; // 限制 @JsonProperty("type") private int type; // 类型 @JsonProperty("num") private int num; // 数量 @JsonProperty("name") private String name; // 名称 @JsonProperty("sold_out_flag") private int soldOutFlag; // 售罄标志 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Region.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) @TableName("pzc_region") //地区 public class Region extends FLBaseEntity { @TableId(value = "regionId", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer regionId; //地区id private String name; //地区名称 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/Tag.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) //活动标签 @TableName("pzc_tag") public class Tag extends FLBaseEntity { @TableId(value = "tag_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer tagId; // ID private String name; // 名称 private String imageUrl; // 图片 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/entity/ViewPager.java ================================================ package top.flya.system.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) @Data @TableName("pzc_view_pager") //轮播图 public class ViewPager extends FLBaseEntity { @TableId(value = "view_pager_id", type = com.baomidou.mybatisplus.annotation.IdType.AUTO) private Integer viewPagerId; //轮播图id private String name; //轮播图名称 private String imageUrl; //轮播图图片Url private String linkUrl; //轮播图链接Url private Integer activityId; //活动id 如果关联活动则不为空 } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/handel/MessageEventHandler.java ================================================ package top.flya.system.handel; import cn.hutool.core.lang.Dict; import cn.hutool.json.JSONUtil; import com.corundumstudio.socketio.AckRequest; import com.corundumstudio.socketio.SocketIOClient; import com.corundumstudio.socketio.annotation.OnConnect; import com.corundumstudio.socketio.annotation.OnDisconnect; import com.corundumstudio.socketio.annotation.OnEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import top.flya.common.utils.JsonUtils; import top.flya.system.config.ClientCache; import top.flya.system.config.Event; import top.flya.system.domain.PzcUser; import top.flya.system.domain.bo.PzcUserTalkBo; import top.flya.system.domain.bo.WxzApplyBo; import top.flya.system.mapper.PzcUserMapper; import top.flya.system.service.IPzcUserTalkService; import javax.annotation.Resource; import java.util.HashMap; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** *

* 消息事件处理 *

* * @author yangkai.shen * @date Created in 2018-12-18 18:57 */ @Component @Slf4j public class MessageEventHandler { @Autowired private ClientCache cache; @Resource private PzcUserMapper userMapper; @Resource private IPzcUserTalkService userTalkService; @Resource private StringRedisTemplate stringRedisTemplate; private final ExecutorService newSingleThreadExecutor = new ThreadPoolExecutor(10, 20, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(100)); /** * 添加connect事件,当客户端发起连接时调用 * * @param client 客户端对象 */ @OnConnect public void onConnect(SocketIOClient client) { if (client != null) { String userId = client.getHandshakeData().getSingleUrlParam("userId"); //我的userId UUID sessionId = client.getSessionId(); if (userId != null) { cache.saveClient(userId, sessionId, client); //查询当前用户是否存在 或者被封 PzcUser pzcUser = userMapper.selectById(userId); if (pzcUser == null || pzcUser.getState() == 0) { log.error("无效连接 该用户不存在 或者被封禁"); client.disconnect(); } log.info("用户上线,【userId】= {},【sessionId】= {}", userId, sessionId); String result = stringRedisTemplate.opsForValue().get("officialMessage:" + userId); if (result != null) { WxzApplyBo wxzApplyBo = JsonUtils.parseObject(result, WxzApplyBo.class); officialMessage(userId, wxzApplyBo); log.info("与对方建立联系时 推送官方消息成功"); } } else { log.error("无效连接"); client.disconnect(); } } else { log.error("客户端为空"); } } /** * 添加disconnect事件,客户端断开连接时调用,刷新客户端信息 * * @param client 客户端对象 */ @OnDisconnect public void onDisconnect(SocketIOClient client) { if (client != null) { String userId = client.getHandshakeData().getSingleUrlParam("userId"); UUID sessionId = client.getSessionId(); if (userId != null) { log.info("客户端断开连接,【userId】= {},【sessionId】= {}", userId, sessionId); cache.deleteSessionClient(userId, client.getSessionId()); } client.disconnect(); } else { log.error("客户端为空"); } } /** * 单聊 */ public boolean sendToSingle(String userId, PzcUserTalkBo message) { HashMap userClient = cache.getUserClient(userId); if (userClient != null) { userClient.forEach(((uuid, socketIOClient) -> { socketIOClient.sendEvent(Event.CHAT, message); })); return true; } return false; } /** * 官方弹窗消息 * * @return */ public boolean officialMessage(String toUserId, WxzApplyBo wxzApplyBo) { HashMap userClient = cache.getUserClient(toUserId); if (userClient != null) { userClient.forEach(((uuid, socketIOClient) -> { socketIOClient.sendEvent(Event.OFFICIAL, wxzApplyBo); })); return true; } return false; } /** * 这里用户给对方发起 一条消息 请求无限制到达 * * @param client * @param request */ @OnEvent(value = Event.OFFICIAL) public void onOfficialEvent(SocketIOClient client, AckRequest request, WxzApplyBo wxzApplyBo) { log.info("用户 {} 刚刚给用户 {} 发起了一条无限制确认到达弹窗", wxzApplyBo.getFromUserId(), wxzApplyBo.getToUserId()); //这里分 用户是否在线 if (officialMessage(String.valueOf(wxzApplyBo.getToUserId()), wxzApplyBo)) { //测试时修改一下 log.info("弹窗消息发送成功"); stringRedisTemplate.opsForValue().set("officialMessage:" + wxzApplyBo.getToUserId(), JsonUtils.toJsonString(wxzApplyBo)); //这里也需要存储到redis request.sendAckData(Dict.create().set("flag", true).set("message", "发送成功")); } else { log.info("弹窗消息发送到Redis 用户不在线"); // 这里需要将消息存储到redis 等待用户上线后推送 stringRedisTemplate.opsForValue().set("officialMessage:" + wxzApplyBo.getToUserId(), JsonUtils.toJsonString(wxzApplyBo)); request.sendAckData(Dict.create().set("flag", false).set("message", "用户不在线~ 对方上线后可见 ")); } } @OnEvent(value = Event.CHAT) public void onChatEvent(SocketIOClient client, AckRequest request, PzcUserTalkBo data) { log.info("用户 {} 刚刚私信了用户 {}:{}", data.getFromUserId(), data.getToUserId(), data.getMessage()); //加异步 newSingleThreadExecutor.execute(() -> { data.setUserId(data.getFromUserId()); userTalkService.insertByBo(data); data.setTalkId(null); data.setUserId(data.getToUserId()); userTalkService.insertByBo(data); }); if (sendToSingle(String.valueOf(data.getToUserId()), data)) { request.sendAckData(Dict.create().set("flag", true).set("message", "发送成功")); } else { request.sendAckData(Dict.create().set("flag", false).set("message", "用户不在线~ 对方上线后可见 ")); } } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/handel/WxPayInitHandel.java ================================================ package top.flya.system.handel; import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier; import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.http.impl.client.CloseableHttpClient; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigInteger; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; /** * Created with IntelliJ IDEA. * * @author: 风离 * @Date: 2022/06/03/23:16 * @Description: */ @Aspect @Component @Slf4j public class WxPayInitHandel { private String privateKey = "-----BEGIN PRIVATE KEY-----\n" + "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpUtNYFC/SVijj\n" + "mOsW2vVIewgnOfB2YuUW8Q0LUbLtN8jfPJrv2BziCWxVciqNtMKO2ONerRtedzar\n" + "D4poTVgDzICefGL8Mxj0PUIGH9dslc9GSsPjc3iynrW0IMFmrx1ItI//7wbWJTCf\n" + "eNWkQa9WmywFUcMQy/oVsWITq7RaoCdYLVQ6w7CrusQ1DOOJ1OoCVA8HB+zqJGKW\n" + "X6dLTTKF14kYNABPHf7lPd8Zv2VIvxLMxqZuSDKZouQjaciXLI4LGdQje9N9j1JE\n" + "cInKIzQnI3AfKTd0TdWR1F+j8QAQCBrHuOLO6WQEQ+N82BZX7KFn+0SVzzH0QjpL\n" + "K+vjHSBZAgMBAAECggEBAKA6qZZC3BIdyGm/7k9dehlRm6CLGnrdEM7J4r8gW8JR\n" + "NLvTPQbUKljX8/VTqOMZ97Z3lYmlJC4bf9cWSLJ05mIJ5niTWpQvwmB1i4ICJbgy\n" + "d8ebvo0BW2kj+OxwxrNl6L9BZrcZOQ3yeXWfQgRCyCqbgmeyPHYroAdhKV9V78CF\n" + "HSHwZOMVPUo/y44w6ig5Fw6pKdGXSzZwjGUyZNpaj4IY3LYWWffuigHJduMzBut2\n" + "v4JJC2TPC9hB6MiR/sVqpKxtnM466BB8pv7hlZ9TJFzdfD5k1UZuheUgUiunajiC\n" + "FeHOEjtSsTWKsAmTg26+Bw5r1apdL3QsLwqH58IVPaUCgYEA1k//XKqWIWG73OKc\n" + "mOH5gjzb3Aq4c6yw5TGPhEkPn7D+7auKuwzuoUrzASeopzp0te7Ow299fNToFWIs\n" + "wEXi4g0gezO7UwpJ6wWNr0iL/wPgYx3jglrPQ+Pvmfoaj4N0mK/pfmXHF8RdIs/M\n" + "p2kn2oQLrJgdeGb4FZM0byX7UcsCgYEAykKJotProJqDogbqsNEIJpBfFlBvxFgb\n" + "IPAp33tBqBh9DiKIOOtISTjPZQIAkzkC8SMRK+HZbF1p2/20bjvTOsi8PKxm/sqS\n" + "v/oPLDfni8pkmhfNU6rr0iNMagpoT9kks4qk9h2dRij1fKJ6pa4TBahJx5wQ3Yu0\n" + "vMEmy8JdwesCgYEAoSGAg7GWMv8Cei6/QosUR4FuZGCDEiWS0p+Sogk0gAJZiWRi\n" + "aARvHkH1traUrTbcLTWhq3sVxFdnLzyjHOTukrr/4uGgQ+0GanfAcTuAVnoZqSv9\n" + "tDKGhyrHKOPMOH7DmVEZovju2cW/qL7Hxk7fsgF5rYipD6+Lct08nRzXekUCgYEA\n" + "s2muWYeOnhox5coo6MujZUHvdwXG/u4AsokXO6xEI24FkEJFf+gFaR5BqiHKjM2n\n" + "tGsc0kY27Y83VfOI17etuZlSkKeFfUIIRs70Io88j53q+11dv3gAU5kIMZAl056U\n" + "lcbIaaD/X7r5d6NRFCKDsSMEv1HLDBrfKghT967kKB0CgYBnynlp2NRtBgtLNURS\n" + "c2xQ7NrlhSiWyLKii/h55PRfxps07w/eqsNALgAqBVDvZt8TcD1IW8/FYv+ZcvMm\n" + "GUA4MNQonauQbLDbkwEHc8MWIpPMJnQYf9YAFlf73qknlikepc+s0mkNkYaZYQYZ\n" + "Q3uWw4iuk4iyxAzWDWrIRPoiRA==\n" + "-----END PRIVATE KEY-----\n"; @Value("${wx.merchantId}") private String merchantId; @Value("${wx.merchantSerialNumber}") private String merchantSerialNumber; @Value("${wx.api3}") private String api3; public CloseableHttpClient setup() throws IOException { log.info("createOrder()方法执行前"); CloseableHttpClient httpClient=null; // 加载商户私钥(privateKey:私钥字符串) PrivateKey merchantPrivateKey = PemUtil .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8"))); log.info(""+merchantId+"--"+merchantSerialNumber+"--"+merchantPrivateKey+"---"+api3); // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥) AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),api3.getBytes("utf-8")); //获取微信支付平台证书证书序列号 BigInteger serialNumber = verifier.getValidCertificate().getSerialNumber(); //转成16进制 String serialnumber = serialNumber.toString(16); //获取微信支付平台证书 X509Certificate validCertificate = verifier.getValidCertificate(); //获取微信支付平台证书公钥 PublicKey publicKey = validCertificate.getPublicKey(); //转成字符串 String prikeyStr = Base64.encodeBase64String(publicKey.getEncoded());//Base64:package System.out.println("prikeyStr:"+prikeyStr); // 初始化httpClient httpClient = WechatPayHttpClientBuilder.create() .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey) .withValidator(new WechatPay2Validator(verifier)).build(); return httpClient; } public void after(CloseableHttpClient httpClient) throws IOException { log.info("createOrder()方法执行后"); httpClient.close(); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityConnArtistMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcActivityConnArtist; import top.flya.system.domain.vo.PzcActivityConnArtistVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动关联艺人Mapper接口 * * @author ruoyi * @date 2023-06-02 */ public interface PzcActivityConnArtistMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityConnIntroMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcActivityConnIntro; import top.flya.system.domain.vo.PzcActivityConnIntroVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动介绍与活动关联Mapper接口 * * @author ruoyi * @date 2023-06-02 */ public interface PzcActivityConnIntroMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityConnTagMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcActivityConnTag; import top.flya.system.domain.vo.PzcActivityConnTagVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动标签与活动关联Mapper接口 * * @author ruoyi * @date 2023-06-03 */ public interface PzcActivityConnTagMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityGroupApplyMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcActivityGroupApply; import top.flya.system.domain.vo.PzcActivityGroupApplyVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动组队申请列Mapper接口 * * @author ruoyi * @date 2023-07-10 */ public interface PzcActivityGroupApplyMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityGroupMapper.java ================================================ package top.flya.system.mapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Param; import org.springframework.web.bind.annotation.PathVariable; import top.flya.system.domain.PzcActivityGroup; import top.flya.system.domain.bo.PzcActivityGroupBo; import top.flya.system.domain.vo.PzcActivityGroupVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动组队Mapper接口 * * @author ruoyi * @date 2023-07-10 */ public interface PzcActivityGroupMapper extends BaseMapperPlus { Page selectDetailsList(Page build,@Param("bo") PzcActivityGroupBo bo); PzcActivityGroupVo selectVoByIdDIY(@Param("groupId") Long groupId); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcActivityMapper.java ================================================ package top.flya.system.mapper; import org.apache.ibatis.annotations.Param; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.PzcActivity; import top.flya.system.domain.vo.PzcActivityVo; import java.util.List; /** * 活动Mapper接口 * * @author ruoyi * @date 2023-06-02 */ public interface PzcActivityMapper extends BaseMapperPlus { List selectActivityByActivityIds(@Param("activityIds") List collect, @Param("classify") Integer type); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcArtistMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcArtist; import top.flya.system.domain.vo.PzcArtistVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 艺人Mapper接口 * * @author flya * @date 2023-06-01 */ public interface PzcArtistMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcIntroMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcIntro; import top.flya.system.domain.vo.PzcIntroVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动介绍Mapper接口 * * @author ruoyi * @date 2023-08-04 */ public interface PzcIntroMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOfficialMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcOfficial; import top.flya.system.domain.vo.PzcOfficialVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 官方消息Mapper接口 * * @author ruoyi * @date 2023-07-27 */ public interface PzcOfficialMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOrderMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcOrder; import top.flya.system.domain.vo.PzcOrderVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 订单Mapper接口 * * @author ruoyi * @date 2023-07-09 */ public interface PzcOrderMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOrganizerMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcOrganizer; import top.flya.system.domain.vo.PzcOrganizerVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动主办方Mapper接口 * * @author ruoyi * @date 2023-06-02 */ public interface PzcOrganizerMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcOrganizerTicketMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcOrganizerTicket; import top.flya.system.domain.vo.PzcOrganizerTicketVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 主办方票务Mapper接口 * * @author ruoyi * @date 2023-07-22 */ public interface PzcOrganizerTicketMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcRegionMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcRegion; import top.flya.system.domain.vo.PzcRegionVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 地区Mapper接口 * * @author ruoyi * @date 2023-07-22 */ public interface PzcRegionMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcTagMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcTag; import top.flya.system.domain.vo.PzcTagVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 活动标签Mapper接口 * * @author ruoyi * @date 2023-06-02 */ public interface PzcTagMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserCollectMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcUserCollect; import top.flya.system.domain.vo.PzcUserCollectVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 用户收藏活动Mapper接口 * * @author ruoyi * @date 2023-07-08 */ public interface PzcUserCollectMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserHistoryMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcUserHistory; import top.flya.system.domain.vo.PzcUserHistoryVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 用户操作历史记录Mapper接口 * * @author ruoyi * @date 2023-07-06 */ public interface PzcUserHistoryMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserMapper.java ================================================ package top.flya.system.mapper; import org.apache.ibatis.annotations.Param; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.PzcUser; import top.flya.system.domain.bo.UpdateMoneyBo; import top.flya.system.domain.vo.PzcUserVo; /** * 用户Mapper接口 * * @author ruoyi * @date 2023-07-09 */ public interface PzcUserMapper extends BaseMapperPlus { int updateMoney(@Param("bo") UpdateMoneyBo bo); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserPhotoMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcUserPhoto; import top.flya.system.domain.vo.PzcUserPhotoVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 用户资料相册Mapper接口 * * @author ruoyi * @date 2023-07-11 */ public interface PzcUserPhotoMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcUserTalkMapper.java ================================================ package top.flya.system.mapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Param; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.PzcUserTalk; import top.flya.system.domain.bo.PzcUserTalkBo; import top.flya.system.domain.vo.PzcUserTalkVo; import java.util.List; /** * 用户聊天Mapper接口 * * @author ruoyi * @date 2023-07-16 */ public interface PzcUserTalkMapper extends BaseMapperPlus { PzcUserTalkVo selectVoPageV1 (@Param("otherUser")Long otherUser, @Param("userId") Long myUserId); Integer selectNotReadCount(@Param("fromUserId") Long userId, @Param("toUserId") Long toUserId,@Param("userId")Long myUserId); Page selectVoPageV2(Page build, @Param("bo") PzcUserTalkBo bo); List selectMyTalkUserIds(@Param("my") Long userId); List selectMyTalkUserIdsV2(@Param("my") Long userId); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/PzcViewPagerMapper.java ================================================ package top.flya.system.mapper; import top.flya.system.domain.PzcViewPager; import top.flya.system.domain.vo.PzcViewPagerVo; import top.flya.common.core.mapper.BaseMapperPlus; /** * 轮播图Mapper接口 * * @author ruoyi * @date 2023-05-23 */ public interface PzcViewPagerMapper extends BaseMapperPlus { } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/mapper/RegionTreeMapper.java ================================================ package top.flya.system.mapper; import org.springframework.stereotype.Component; import top.flya.system.domain.vo.PzcRegionVo; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Component public class RegionTreeMapper { public Map> buildTree(List pzcRegionVos) { List tree = new ArrayList<>(); Map nodeMap = new HashMap<>(); for (PzcRegionVo node : pzcRegionVos) { nodeMap.put(node.getBase(), node); } for (PzcRegionVo node : pzcRegionVos) { String parentBase = getParentBase(node); if (parentBase != null) { PzcRegionVo parentNode = nodeMap.get(parentBase); if (parentNode != null) { parentNode.getChildren().add(node); } } else { tree.add(node); } } Map> resultMap = new HashMap<>(); for (PzcRegionVo node : tree) { String base = node.getBase(); if (resultMap.containsKey(base)) { resultMap.get(base).add(node); } else { List nodeList = new ArrayList<>(); nodeList.add(node); resultMap.put(base, nodeList); } } resultMap.put("全部",pzcRegionVos); return resultMap; } private String getParentBase(PzcRegionVo node) { String base = node.getBase(); if (base != null && base.contains("-")) { return base.substring(0, base.lastIndexOf("-")); } return null; } // public static void main(String[] args) { // List pzcRegionVos = new ArrayList<>(); //// 添加示例数据 // pzcRegionVos.add(new PzcRegionVo(1L, "江苏省", "南京市", "", null, null, null)); // pzcRegionVos.add(new PzcRegionVo(2L, "江苏省", "苏州市", "", null, null, null)); // pzcRegionVos.add(new PzcRegionVo(3L, "上海市", "上海市", "", null, null, null)); // // // System.out.println(JSONUtil.toJsonPrettyStr( buildTree(pzcRegionVos))); // } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityConnArtistService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcActivityConnArtist; import top.flya.system.domain.vo.PzcActivityConnArtistVo; import top.flya.system.domain.bo.PzcActivityConnArtistBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 活动关联艺人Service接口 * * @author ruoyi * @date 2023-06-02 */ public interface IPzcActivityConnArtistService { /** * 查询活动关联艺人 */ PzcActivityConnArtistVo queryById(Integer activityConnArtistId); /** * 查询活动关联艺人列表 */ TableDataInfo queryPageList(PzcActivityConnArtistBo bo, PageQuery pageQuery); /** * 查询活动关联艺人列表 */ List queryList(PzcActivityConnArtistBo bo); /** * 新增活动关联艺人 */ Boolean insertByBo(PzcActivityConnArtistBo bo); /** * 修改活动关联艺人 */ Boolean updateByBo(PzcActivityConnArtistBo bo); /** * 校验并批量删除活动关联艺人信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityConnIntroService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcActivityConnIntro; import top.flya.system.domain.vo.PzcActivityConnIntroVo; import top.flya.system.domain.bo.PzcActivityConnIntroBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 活动介绍与活动关联Service接口 * * @author ruoyi * @date 2023-06-02 */ public interface IPzcActivityConnIntroService { /** * 查询活动介绍与活动关联 */ PzcActivityConnIntroVo queryById(Integer activityConnIntroId); /** * 查询活动介绍与活动关联列表 */ TableDataInfo queryPageList(PzcActivityConnIntroBo bo, PageQuery pageQuery); /** * 查询活动介绍与活动关联列表 */ List queryList(PzcActivityConnIntroBo bo); /** * 新增活动介绍与活动关联 */ Boolean insertByBo(PzcActivityConnIntroBo bo); /** * 修改活动介绍与活动关联 */ Boolean updateByBo(PzcActivityConnIntroBo bo); /** * 校验并批量删除活动介绍与活动关联信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityConnTagService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcActivityConnTag; import top.flya.system.domain.vo.PzcActivityConnTagVo; import top.flya.system.domain.bo.PzcActivityConnTagBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 活动标签与活动关联Service接口 * * @author ruoyi * @date 2023-06-03 */ public interface IPzcActivityConnTagService { /** * 查询活动标签与活动关联 */ PzcActivityConnTagVo queryById(Integer activityConnTagId); /** * 查询活动标签与活动关联列表 */ TableDataInfo queryPageList(PzcActivityConnTagBo bo, PageQuery pageQuery); /** * 查询活动标签与活动关联列表 */ List queryList(PzcActivityConnTagBo bo); /** * 新增活动标签与活动关联 */ Boolean insertByBo(PzcActivityConnTagBo bo); /** * 修改活动标签与活动关联 */ Boolean updateByBo(PzcActivityConnTagBo bo); /** * 校验并批量删除活动标签与活动关联信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityGroupApplyService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.bo.PzcActivityGroupApplyBo; import top.flya.system.domain.vo.PzcActivityGroupApplyVo; import java.util.Collection; import java.util.List; /** * 活动组队申请列Service接口 * * @author ruoyi * @date 2023-07-10 */ public interface IPzcActivityGroupApplyService { /** * 查询活动组队申请列 */ PzcActivityGroupApplyVo queryById(Long applyId); /** * 查询活动组队申请列列表 */ TableDataInfo queryPageList(PzcActivityGroupApplyBo bo, PageQuery pageQuery); /** * 查询活动组队申请列列表 */ List queryList(PzcActivityGroupApplyBo bo); /** * 新增活动组队申请列 */ Boolean insertByBo(PzcActivityGroupApplyBo bo); /** * 修改活动组队申请列 */ Boolean updateByBo(PzcActivityGroupApplyBo bo); /** * 校验并批量删除活动组队申请列信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); boolean queryByUserIdAndActivityId(Long userId, Long activityId); Integer updateStatus(Long applyId, int i); List queryListByGroupIds(List groupIds); PzcActivityGroupApplyVo queryByUserIdAndGroupId(Long userId, Long groupId); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityGroupService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcActivityGroup; import top.flya.system.domain.vo.PzcActivityGroupVo; import top.flya.system.domain.bo.PzcActivityGroupBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 活动组队Service接口 * * @author ruoyi * @date 2023-07-10 */ public interface IPzcActivityGroupService { /** * 查询活动组队 */ PzcActivityGroupVo queryById(Long groupId); /** * 查询活动组队列表 */ TableDataInfo queryPageList(PzcActivityGroupBo bo, PageQuery pageQuery); /** * 查询活动组队列表 */ List queryList(PzcActivityGroupBo bo); /** * 新增活动组队 */ Boolean insertByBo(PzcActivityGroupBo bo); /** * 修改活动组队 */ Boolean updateByBo(PzcActivityGroupBo bo); /** * 校验并批量删除活动组队信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); boolean checkActivity(Long activityId); boolean checkGroup(Long userId, Long activityId); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcActivityService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcActivity; import top.flya.system.domain.vo.PzcActivityVo; import top.flya.system.domain.bo.PzcActivityBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 活动Service接口 * * @author ruoyi * @date 2023-06-02 */ public interface IPzcActivityService { /** * 查询活动 */ PzcActivityVo queryById(Integer activityId); /** * 查询活动列表 */ TableDataInfo queryPageList(PzcActivityBo bo, PageQuery pageQuery); /** * 查询活动列表 微信 * */ TableDataInfo queryPageListWx(PzcActivityBo bo, PageQuery pageQuery); /** * 查询活动列表 */ List queryList(PzcActivityBo bo); /** * 新增活动 */ Boolean insertByBo(PzcActivityBo bo); /** * 修改活动 */ Boolean updateByBo(PzcActivityBo bo); /** * 校验并批量删除活动信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcArtistService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcArtist; import top.flya.system.domain.vo.PzcArtistVo; import top.flya.system.domain.bo.PzcArtistBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 艺人Service接口 * * @author flya * @date 2023-06-01 */ public interface IPzcArtistService { /** * 查询艺人 */ PzcArtistVo queryById(Long artistId); /** * 查询艺人列表 */ TableDataInfo queryPageList(PzcArtistBo bo, PageQuery pageQuery); /** * 查询艺人列表 */ List queryList(PzcArtistBo bo); /** * 新增艺人 */ Boolean insertByBo(PzcArtistBo bo); /** * 修改艺人 */ Boolean updateByBo(PzcArtistBo bo); /** * 校验并批量删除艺人信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcIntroService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.bo.PzcIntroBo; import top.flya.system.domain.vo.PzcIntroVo; import java.util.Collection; import java.util.List; /** * 活动介绍Service接口 * * @author ruoyi * @date 2023-08-04 */ public interface IPzcIntroService { /** * 查询活动介绍 */ PzcIntroVo queryById(Long introId); /** * 查询活动介绍列表 */ TableDataInfo queryPageList(PzcIntroBo bo, PageQuery pageQuery); /** * 查询活动介绍列表 */ List queryList(PzcIntroBo bo); /** * 新增活动介绍 */ Boolean insertByBo(PzcIntroBo bo); /** * 修改活动介绍 */ Boolean updateByBo(PzcIntroBo bo); /** * 校验并批量删除活动介绍信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOfficialService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.bo.PzcOfficialBo; import top.flya.system.domain.vo.PzcOfficialVo; import java.util.Collection; import java.util.List; /** * 官方消息Service接口 * * @author ruoyi * @date 2023-07-27 */ public interface IPzcOfficialService { /** * 查询官方消息 */ PzcOfficialVo queryById(Long officialId); /** * 查询官方消息列表 */ TableDataInfo queryPageList(PzcOfficialBo bo, PageQuery pageQuery); /** * 查询官方消息列表 */ List queryList(PzcOfficialBo bo); /** * 新增官方消息 */ Boolean insertByBo(PzcOfficialBo bo); /** * 修改官方消息 */ Boolean updateByBo(PzcOfficialBo bo); /** * 校验并批量删除官方消息信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); Integer read(Integer officialId); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOrderService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcOrder; import top.flya.system.domain.vo.PzcOrderVo; import top.flya.system.domain.bo.PzcOrderBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 订单Service接口 * * @author ruoyi * @date 2023-07-09 */ public interface IPzcOrderService { /** * 查询订单 */ PzcOrderVo queryById(Long orderId); /** * 查询订单列表 */ TableDataInfo queryPageList(PzcOrderBo bo, PageQuery pageQuery); /** * 查询订单列表 */ List queryList(PzcOrderBo bo); /** * 新增订单 */ Boolean insertByBo(PzcOrderBo bo); /** * 修改订单 */ Boolean updateByBo(PzcOrderBo bo); /** * 校验并批量删除订单信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOrganizerService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcOrganizer; import top.flya.system.domain.vo.PzcOrganizerVo; import top.flya.system.domain.bo.PzcOrganizerBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 活动主办方Service接口 * * @author ruoyi * @date 2023-06-02 */ public interface IPzcOrganizerService { /** * 查询活动主办方 */ PzcOrganizerVo queryById(Long organizerId); /** * 查询活动主办方列表 */ TableDataInfo queryPageList(PzcOrganizerBo bo, PageQuery pageQuery); /** * 查询活动主办方列表 */ List queryList(PzcOrganizerBo bo); /** * 新增活动主办方 */ Boolean insertByBo(PzcOrganizerBo bo); /** * 修改活动主办方 */ Boolean updateByBo(PzcOrganizerBo bo); /** * 校验并批量删除活动主办方信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcOrganizerTicketService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcOrganizerTicket; import top.flya.system.domain.vo.PzcOrganizerTicketVo; import top.flya.system.domain.bo.PzcOrganizerTicketBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 主办方票务Service接口 * * @author ruoyi * @date 2023-07-22 */ public interface IPzcOrganizerTicketService { /** * 查询主办方票务 */ PzcOrganizerTicketVo queryById(Long organizerTicketId); /** * 查询主办方票务列表 */ TableDataInfo queryPageList(PzcOrganizerTicketBo bo, PageQuery pageQuery); /** * 查询主办方票务列表 */ List queryList(PzcOrganizerTicketBo bo); /** * 新增主办方票务 */ Boolean insertByBo(PzcOrganizerTicketBo bo); /** * 修改主办方票务 */ Boolean updateByBo(PzcOrganizerTicketBo bo); /** * 校验并批量删除主办方票务信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcRegionService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.bo.PzcRegionBo; import top.flya.system.domain.vo.PzcRegionVo; import java.util.Collection; import java.util.List; /** * 地区Service接口 * * @author ruoyi * @date 2023-07-22 */ public interface IPzcRegionService { /** * 查询地区 */ PzcRegionVo queryById(Long regionId); /** * 查询地区列表 */ TableDataInfo queryPageList(PzcRegionBo bo, PageQuery pageQuery); /** * 查询地区列表 */ List queryList(PzcRegionBo bo); /** * 新增地区 */ Boolean insertByBo(PzcRegionBo bo); /** * 修改地区 */ Boolean updateByBo(PzcRegionBo bo); /** * 校验并批量删除地区信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcTagService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcTag; import top.flya.system.domain.vo.PzcTagVo; import top.flya.system.domain.bo.PzcTagBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 活动标签Service接口 * * @author ruoyi * @date 2023-06-02 */ public interface IPzcTagService { /** * 查询活动标签 */ PzcTagVo queryById(Long tagId); /** * 查询活动标签列表 */ TableDataInfo queryPageList(PzcTagBo bo, PageQuery pageQuery); /** * 查询活动标签列表 */ List queryList(PzcTagBo bo); /** * 新增活动标签 */ Boolean insertByBo(PzcTagBo bo); /** * 修改活动标签 */ Boolean updateByBo(PzcTagBo bo); /** * 校验并批量删除活动标签信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserCollectService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcUserCollect; import top.flya.system.domain.vo.PzcUserCollectVo; import top.flya.system.domain.bo.PzcUserCollectBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 用户收藏活动Service接口 * * @author ruoyi * @date 2023-07-08 */ public interface IPzcUserCollectService { /** * 查询用户收藏活动 */ PzcUserCollectVo queryById(Long collectId); /** * 查询用户收藏活动列表 */ TableDataInfo queryPageList(PzcUserCollectBo bo, PageQuery pageQuery); /** * 查询用户收藏活动列表 */ List queryList(PzcUserCollectBo bo); /** * 新增用户收藏活动 */ Boolean insertByBo(PzcUserCollectBo bo); /** * 修改用户收藏活动 */ Boolean updateByBo(PzcUserCollectBo bo); /** * 校验并批量删除用户收藏活动信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserHistoryService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcUserHistory; import top.flya.system.domain.vo.PzcUserHistoryVo; import top.flya.system.domain.bo.PzcUserHistoryBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 用户操作历史记录Service接口 * * @author ruoyi * @date 2023-07-06 */ public interface IPzcUserHistoryService { /** * 查询用户操作历史记录 */ PzcUserHistoryVo queryById(Long historyId); /** * 查询用户操作历史记录列表 */ TableDataInfo queryPageList(PzcUserHistoryBo bo, PageQuery pageQuery); /** * 查询用户操作历史记录列表 */ List queryList(PzcUserHistoryBo bo); /** * 新增用户操作历史记录 */ Boolean insertByBo(PzcUserHistoryBo bo); /** * 修改用户操作历史记录 */ Boolean updateByBo(PzcUserHistoryBo bo); /** * 校验并批量删除用户操作历史记录信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserPhotoService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcUserPhoto; import top.flya.system.domain.vo.PzcUserPhotoVo; import top.flya.system.domain.bo.PzcUserPhotoBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 用户资料相册Service接口 * * @author ruoyi * @date 2023-07-11 */ public interface IPzcUserPhotoService { /** * 查询用户资料相册 */ PzcUserPhotoVo queryById(Long photoId); /** * 查询用户资料相册列表 */ TableDataInfo queryPageList(PzcUserPhotoBo bo, PageQuery pageQuery); /** * 查询用户资料相册列表 */ List queryList(PzcUserPhotoBo bo); /** * 新增用户资料相册 */ Boolean insertByBo(PzcUserPhotoBo bo); /** * 修改用户资料相册 */ Boolean updateByBo(PzcUserPhotoBo bo); /** * 校验并批量删除用户资料相册信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.bo.PzcUserBo; import top.flya.system.domain.bo.UpdateMoneyBo; import top.flya.system.domain.vo.PzcUserVo; import java.util.Collection; import java.util.List; /** * 用户Service接口 * * @author ruoyi * @date 2023-07-09 */ public interface IPzcUserService { /** * 查询用户 */ PzcUserVo queryById(Long userId); /** * 查询用户列表 */ TableDataInfo queryPageList(PzcUserBo bo, PageQuery pageQuery); /** * 查询用户列表 */ List queryList(PzcUserBo bo); /** * 新增用户 */ Boolean insertByBo(PzcUserBo bo); /** * 修改用户 */ Boolean updateByBo(PzcUserBo bo); /** * 校验并批量删除用户信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); int updateMoney(UpdateMoneyBo bo); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcUserTalkService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcUserTalk; import top.flya.system.domain.vo.PzcUserTalkVo; import top.flya.system.domain.bo.PzcUserTalkBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 用户聊天Service接口 * * @author ruoyi * @date 2023-07-16 */ public interface IPzcUserTalkService { /** * 查询用户聊天 */ PzcUserTalkVo queryById(Long talkId); /** * 查询用户聊天列表 */ TableDataInfo queryPageList(PzcUserTalkBo bo, PageQuery pageQuery); /** * 查询用户聊天列表 */ List queryList(PzcUserTalkBo bo); /** * 新增用户聊天 */ Boolean insertByBo(PzcUserTalkBo bo); /** * 修改用户聊天 */ Boolean updateByBo(PzcUserTalkBo bo); /** * 校验并批量删除用户聊天信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); TableDataInfo queryMyPageList(PzcUserTalkBo bo, PageQuery pageQuery); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/IPzcViewPagerService.java ================================================ package top.flya.system.service; import top.flya.system.domain.PzcViewPager; import top.flya.system.domain.vo.PzcViewPagerVo; import top.flya.system.domain.bo.PzcViewPagerBo; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; /** * 轮播图Service接口 * * @author ruoyi * @date 2023-05-23 */ public interface IPzcViewPagerService { /** * 查询轮播图 */ PzcViewPagerVo queryById(Integer viewPagerId); /** * 查询轮播图列表 */ TableDataInfo queryPageList(PzcViewPagerBo bo, PageQuery pageQuery); /** * 查询轮播图列表 */ List queryList(PzcViewPagerBo bo); /** * 新增轮播图 */ Boolean insertByBo(PzcViewPagerBo bo); /** * 修改轮播图 */ Boolean updateByBo(PzcViewPagerBo bo); /** * 校验并批量删除轮播图信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityConnArtistServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.domain.bo.PzcActivityConnArtistBo; import top.flya.system.domain.vo.PzcActivityConnArtistVo; import top.flya.system.domain.PzcActivityConnArtist; import top.flya.system.mapper.PzcActivityConnArtistMapper; import top.flya.system.service.IPzcActivityConnArtistService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 活动关联艺人Service业务层处理 * * @author ruoyi * @date 2023-06-02 */ @RequiredArgsConstructor @Service public class PzcActivityConnArtistServiceImpl implements IPzcActivityConnArtistService { private final PzcActivityConnArtistMapper baseMapper; /** * 查询活动关联艺人 */ @Override public PzcActivityConnArtistVo queryById(Integer activityConnArtistId){ return baseMapper.selectVoById(activityConnArtistId); } /** * 查询活动关联艺人列表 */ @Override public TableDataInfo queryPageList(PzcActivityConnArtistBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询活动关联艺人列表 */ @Override public List queryList(PzcActivityConnArtistBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcActivityConnArtistBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getActivityId() != null, PzcActivityConnArtist::getActivityId, bo.getActivityId()); lqw.eq(bo.getArtistId() != null, PzcActivityConnArtist::getArtistId, bo.getArtistId()); lqw.eq(bo.getCreateTime() != null, PzcActivityConnArtist::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcActivityConnArtist::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增活动关联艺人 */ @Override public Boolean insertByBo(PzcActivityConnArtistBo bo) { PzcActivityConnArtist add = BeanUtil.toBean(bo, PzcActivityConnArtist.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setActivityConnArtistId(add.getActivityConnArtistId()); } return flag; } /** * 修改活动关联艺人 */ @Override public Boolean updateByBo(PzcActivityConnArtistBo bo) { PzcActivityConnArtist update = BeanUtil.toBean(bo, PzcActivityConnArtist.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcActivityConnArtist entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动关联艺人 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityConnIntroServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.domain.bo.PzcActivityConnIntroBo; import top.flya.system.domain.vo.PzcActivityConnIntroVo; import top.flya.system.domain.PzcActivityConnIntro; import top.flya.system.mapper.PzcActivityConnIntroMapper; import top.flya.system.service.IPzcActivityConnIntroService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 活动介绍与活动关联Service业务层处理 * * @author ruoyi * @date 2023-06-02 */ @RequiredArgsConstructor @Service public class PzcActivityConnIntroServiceImpl implements IPzcActivityConnIntroService { private final PzcActivityConnIntroMapper baseMapper; /** * 查询活动介绍与活动关联 */ @Override public PzcActivityConnIntroVo queryById(Integer activityConnIntroId){ return baseMapper.selectVoById(activityConnIntroId); } /** * 查询活动介绍与活动关联列表 */ @Override public TableDataInfo queryPageList(PzcActivityConnIntroBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询活动介绍与活动关联列表 */ @Override public List queryList(PzcActivityConnIntroBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcActivityConnIntroBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getActivityConnIntroId() != null, PzcActivityConnIntro::getActivityConnIntroId, bo.getActivityConnIntroId()); lqw.eq(bo.getActivityId() != null, PzcActivityConnIntro::getActivityId, bo.getActivityId()); lqw.eq(bo.getIntroId() != null, PzcActivityConnIntro::getIntroId, bo.getIntroId()); return lqw; } /** * 新增活动介绍与活动关联 */ @Override public Boolean insertByBo(PzcActivityConnIntroBo bo) { PzcActivityConnIntro add = BeanUtil.toBean(bo, PzcActivityConnIntro.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setActivityConnIntroId(add.getActivityConnIntroId()); } return flag; } /** * 修改活动介绍与活动关联 */ @Override public Boolean updateByBo(PzcActivityConnIntroBo bo) { PzcActivityConnIntro update = BeanUtil.toBean(bo, PzcActivityConnIntro.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcActivityConnIntro entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动介绍与活动关联 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityConnTagServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.domain.bo.PzcActivityConnTagBo; import top.flya.system.domain.vo.PzcActivityConnTagVo; import top.flya.system.domain.PzcActivityConnTag; import top.flya.system.mapper.PzcActivityConnTagMapper; import top.flya.system.service.IPzcActivityConnTagService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 活动标签与活动关联Service业务层处理 * * @author ruoyi * @date 2023-06-03 */ @RequiredArgsConstructor @Service public class PzcActivityConnTagServiceImpl implements IPzcActivityConnTagService { private final PzcActivityConnTagMapper baseMapper; /** * 查询活动标签与活动关联 */ @Override public PzcActivityConnTagVo queryById(Integer activityConnTagId){ return baseMapper.selectVoById(activityConnTagId); } /** * 查询活动标签与活动关联列表 */ @Override public TableDataInfo queryPageList(PzcActivityConnTagBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询活动标签与活动关联列表 */ @Override public List queryList(PzcActivityConnTagBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcActivityConnTagBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getActivityConnTagId() != null, PzcActivityConnTag::getActivityConnTagId, bo.getActivityConnTagId()); lqw.eq(bo.getActivityId() != null, PzcActivityConnTag::getActivityId, bo.getActivityId()); lqw.eq(bo.getTagId() != null, PzcActivityConnTag::getTagId, bo.getTagId()); lqw.eq(bo.getCreateTime() != null, PzcActivityConnTag::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcActivityConnTag::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增活动标签与活动关联 */ @Override public Boolean insertByBo(PzcActivityConnTagBo bo) { PzcActivityConnTag add = BeanUtil.toBean(bo, PzcActivityConnTag.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setActivityConnTagId(add.getActivityConnTagId()); } return flag; } /** * 修改活动标签与活动关联 */ @Override public Boolean updateByBo(PzcActivityConnTagBo bo) { PzcActivityConnTag update = BeanUtil.toBean(bo, PzcActivityConnTag.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcActivityConnTag entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动标签与活动关联 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityGroupApplyServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.domain.bo.PzcActivityGroupApplyBo; import top.flya.system.domain.vo.PzcActivityGroupApplyVo; import top.flya.system.domain.PzcActivityGroupApply; import top.flya.system.mapper.PzcActivityGroupApplyMapper; import top.flya.system.service.IPzcActivityGroupApplyService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 活动组队申请列Service业务层处理 * * @author ruoyi * @date 2023-07-10 */ @RequiredArgsConstructor @Service public class PzcActivityGroupApplyServiceImpl implements IPzcActivityGroupApplyService { private final PzcActivityGroupApplyMapper baseMapper; /** * 查询活动组队申请列 */ @Override public PzcActivityGroupApplyVo queryById(Long applyId){ return baseMapper.selectVoById(applyId); } /** * 查询活动组队申请列列表 */ @Override public TableDataInfo queryPageList(PzcActivityGroupApplyBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询活动组队申请列列表 */ @Override public List queryList(PzcActivityGroupApplyBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcActivityGroupApplyBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getApplyId() != null, PzcActivityGroupApply::getApplyId, bo.getApplyId()); lqw.eq(bo.getUserId() != null, PzcActivityGroupApply::getUserId, bo.getUserId()); lqw.eq(bo.getActivityId() != null, PzcActivityGroupApply::getActivityId, bo.getActivityId()); lqw.eq(bo.getGroupId() != null, PzcActivityGroupApply::getGroupId, bo.getGroupId()); lqw.eq(bo.getGroupType() != null, PzcActivityGroupApply::getGroupType, bo.getGroupType()); lqw.eq(bo.getMoney() != null, PzcActivityGroupApply::getMoney, bo.getMoney()); // lqw.eq(bo.getApplyStatus() != null, PzcActivityGroupApply::getApplyStatus, bo.getApplyStatus()); lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcActivityGroupApply::getMessage, bo.getMessage()); lqw.eq(bo.getCreateTime() != null, PzcActivityGroupApply::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcActivityGroupApply::getUpdateTime, bo.getUpdateTime()); lqw.eq(bo.getWxz()!=null,PzcActivityGroupApply::getWxz,bo.getWxz()); return lqw; } /** * 新增活动组队申请列 */ @Override public Boolean insertByBo(PzcActivityGroupApplyBo bo) { PzcActivityGroupApply add = BeanUtil.toBean(bo, PzcActivityGroupApply.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setApplyId(add.getApplyId()); } return flag; } /** * 修改活动组队申请列 */ @Override public Boolean updateByBo(PzcActivityGroupApplyBo bo) { PzcActivityGroupApply update = BeanUtil.toBean(bo, PzcActivityGroupApply.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcActivityGroupApply entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动组队申请列 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } @Override public boolean queryByUserIdAndActivityId(Long userId, Long activityId) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcActivityGroupApply::getUserId, userId); lqw.eq(PzcActivityGroupApply::getActivityId, activityId); List list = baseMapper.selectList(lqw); if (list != null && list.size() > 0) { return true; } return false; } @Override public Integer updateStatus(Long applyId, int i) { PzcActivityGroupApply apply = baseMapper.selectById(applyId); if (apply != null) { apply.setApplyStatus(i); return baseMapper.updateById(apply); } return null; } @Override public List queryListByGroupIds(List groupIds) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.in(PzcActivityGroupApply::getGroupId, groupIds); lqw.orderByDesc(PzcActivityGroupApply::getCreateTime); return baseMapper.selectVoList(lqw); } @Override public PzcActivityGroupApplyVo queryByUserIdAndGroupId(Long userId, Long groupId) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcActivityGroupApply::getUserId, userId); lqw.eq(PzcActivityGroupApply::getGroupId, groupId); return baseMapper.selectVoOne(lqw); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityGroupServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.utils.JsonUtils; import top.flya.system.domain.PzcActivityGroup; import top.flya.system.domain.PzcActivityGroupApply; import top.flya.system.domain.PzcRegion; import top.flya.system.domain.PzcUserPhoto; import top.flya.system.domain.bo.PzcActivityGroupBo; import top.flya.system.domain.vo.PzcActivityGroupVo; import top.flya.system.domain.vo.PzcUserVo; import top.flya.system.mapper.*; import top.flya.system.service.IPzcActivityGroupService; import top.flya.system.utils.gaode.GaoDeMapUtil; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 活动组队Service业务层处理 * * @author ruoyi * @date 2023-07-10 */ @RequiredArgsConstructor @Service @Slf4j public class PzcActivityGroupServiceImpl implements IPzcActivityGroupService { private final PzcActivityGroupMapper baseMapper; private final PzcActivityMapper pzcActivityMapper; private final PzcUserPhotoMapper pzcUserPhotoMapper; private final PzcActivityGroupApplyMapper pzcActivityGroupApplyMapper; private final PzcRegionMapper pzcRegionMapper; private final GaoDeMapUtil gaoDeMapUtil; /** * 查询活动组队 */ @Override public PzcActivityGroupVo queryById(Long groupId) { PzcActivityGroupVo pzcActivityGroupVo = baseMapper.selectVoByIdDIY(groupId); if (pzcActivityGroupVo == null) { return null; } if (pzcActivityGroupVo.getAuth() == 2) { log.info("私密组队,不返回用户信息"); pzcActivityGroupVo.setUser(getPrivateUser(pzcActivityGroupVo.getUser())); pzcActivityGroupVo.setPhoto(null); } else { List userPhotos = pzcUserPhotoMapper.selectList(new QueryWrapper().eq("user_id", pzcActivityGroupVo.getUserId())); pzcActivityGroupVo.setPhoto(userPhotos); if (pzcActivityGroupVo.getAuth() == 1) //权限 1只返回一张图片 即头像 { pzcActivityGroupVo.setPhoto(null); // } } if (pzcActivityMapper.selectVoById(pzcActivityGroupVo.getActivityId()) == null) { Integer region = pzcActivityGroupVo.getRegion(); PzcRegion pzcRegion = pzcRegionMapper.selectById(region); if (pzcRegion != null) { pzcActivityGroupVo.setActivityTitle("【" + pzcRegion.getName() + "】"); } } else { pzcActivityGroupVo.setActivityTitle(pzcActivityMapper.selectVoById(pzcActivityGroupVo.getActivityId()).getTitle()); } pzcActivityGroupVo.setAddress(pzcActivityGroupVo.getAddress().substring(pzcActivityGroupVo.getAddress().indexOf("】") + 1)); return pzcActivityGroupVo; } public PzcUserVo getPrivateUser(PzcUserVo pzcUser) { PzcUserVo pzcUserVo = new PzcUserVo(); pzcUserVo.setUserId(pzcUser.getUserId()); pzcUserVo.setOpenid(pzcUser.getOpenid()); pzcUserVo.setMoney(null); pzcUserVo.setUserLevel(pzcUser.getUserLevel()); pzcUserVo.setIntegration(null); pzcUserVo.setIntegrationNow(null); pzcUserVo.setRealname(null); pzcUserVo.setNickname("匿名用户"); pzcUserVo.setSex(null); pzcUserVo.setPhone(null); pzcUserVo.setAvatar("https://img.flya.top/mac/202309261521392.png"); pzcUserVo.setAddress(null); pzcUserVo.setIntro(null); pzcUserVo.setAge(null); pzcUserVo.setConstellation(null); pzcUserVo.setMbti(null); pzcUserVo.setHobby(null); pzcUserVo.setSchool(null); pzcUserVo.setOccupation(null); pzcUserVo.setMusicStyle(null); pzcUserVo.setState(null); pzcUserVo.setExemptCancel(null); return pzcUserVo; } /** * 查询活动组队列表 */ @Override public TableDataInfo queryPageList(PzcActivityGroupBo bo, PageQuery pageQuery) { log.info("查询活动组队列表 bo is {}", JsonUtils.toJsonString(bo)); if(bo.getUserLevel()!=null) { pageQuery.setIsAsc("desc"); pageQuery.setOrderByColumn("u.user_level"); } Page result = baseMapper.selectDetailsList(pageQuery.build(), bo); ArrayList pzcActivityGroupVos = new ArrayList<>(); Pattern pattern = Pattern.compile("【(.*?)】(.*?)"); result.getRecords().forEach( pzcActivityGroupVo -> { pzcActivityGroupVos.add(pzcActivityGroupVo); if (pzcActivityGroupVo.getAuth() == 2) { log.info("私密组队,返回用户信息的部分信息"); pzcActivityGroupVo.setUser(getPrivateUser(pzcActivityGroupVo.getUser())); }//如果是私密组队,不返回用户信息 //查询当前组队 是否正在进行中 如果是 则 不进入组队大厅 Long groupId = pzcActivityGroupVo.getGroupId(); Long count = pzcActivityGroupApplyMapper.selectCount(new QueryWrapper().eq("group_id", groupId).notIn("apply_status", -1, 0, 13, 14, 15)); if (count > 0) { if(bo.getUserId()==null) //只是不对大厅展示 { //从列表中移除这个对象、 log.info("当前组队正在进行中,不返回组队信息 groupId is {}", pzcActivityGroupVo.getGroupId()); pzcActivityGroupVos.remove(pzcActivityGroupVo); } } if (bo.getLongitudeAndLatitude() != null && !bo.getLongitudeAndLatitude().isEmpty()) { //计算距离 String jwdFromAddress = pzcActivityGroupVo.getAddress(); Matcher matcher = pattern.matcher(jwdFromAddress); if (matcher.find()) { // 获取经纬度 log.info("从数据库地址经纬度 is {}", jwdFromAddress); jwdFromAddress = matcher.group(1); pzcActivityGroupVo.setAddress(pzcActivityGroupVo.getAddress().substring(pzcActivityGroupVo.getAddress().indexOf("】") + 1)); } else { log.info("调用API获取经纬度 is {}", jwdFromAddress); jwdFromAddress = gaoDeMapUtil.getLonLat(pzcActivityGroupVo.getAddress()).getData().toString(); //经纬度 } String distance = gaoDeMapUtil.getDistance(bo.getLongitudeAndLatitude(), jwdFromAddress).getData().toString(); log.info("离我【{}】米", distance); //计算距离 BigDecimal distanceStr = new BigDecimal(distance).divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP); //保留两位小数 pzcActivityGroupVo.setDistance(distanceStr); } } ); if (bo.getDistance() != null) { // log.info("按照距离排序前: {}",JSONUtil.toJsonPrettyStr(pzcActivityGroupVos)); pzcActivityGroupVos.sort(Comparator.comparing(PzcActivityGroupVo::getDistance)); //按照距离远到近排序 // log.info("按照距离排序后: {}",JSONUtil.toJsonPrettyStr(pzcActivityGroupVos)); } //查询当前组队 是否正在进行中 如果是 则 不进入组队大厅 return TableDataInfo.build(pzcActivityGroupVos); } /** * 查询活动组队列表 */ @Override public List queryList(PzcActivityGroupBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcActivityGroupBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getActivityId() != null, PzcActivityGroup::getActivityId, bo.getActivityId()); lqw.eq(bo.getUserId() != null, PzcActivityGroup::getUserId, bo.getUserId()); lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcActivityGroup::getTitle, bo.getTitle()); lqw.eq(bo.getMoney() != null, PzcActivityGroup::getMoney, bo.getMoney()); lqw.eq(bo.getGroupType() != null, PzcActivityGroup::getGroupType, bo.getGroupType()); lqw.eq(StringUtils.isNotBlank(bo.getAddress()), PzcActivityGroup::getAddress, bo.getAddress()); lqw.eq(bo.getActivityTime() != null, PzcActivityGroup::getActivityTime, bo.getActivityTime()); lqw.eq(bo.getAuth() != null, PzcActivityGroup::getAuth, bo.getAuth()); lqw.eq(bo.getCreateTime() != null, PzcActivityGroup::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcActivityGroup::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增活动组队 */ @Override public Boolean insertByBo(PzcActivityGroupBo bo) { PzcActivityGroup add = BeanUtil.toBean(bo, PzcActivityGroup.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setGroupId(add.getGroupId()); } return flag; } /** * 修改活动组队 */ @Override public Boolean updateByBo(PzcActivityGroupBo bo) { PzcActivityGroup update = BeanUtil.toBean(bo, PzcActivityGroup.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcActivityGroup entity) { //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动组队 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } @Override public boolean checkActivity(Long activityId) { return pzcActivityMapper.selectVoById(activityId) != null; } @Override public boolean checkGroup(Long userId, Long activityId) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcActivityGroup::getUserId, userId); lqw.eq(PzcActivityGroup::getActivityId, activityId); // return baseMapper.selectCount(lqw) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcActivityServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.common.BatchUtils; import top.flya.system.domain.*; import top.flya.system.domain.bo.PzcActivityBo; import top.flya.system.domain.vo.PzcActivityVo; import top.flya.system.mapper.*; import top.flya.system.service.IPzcActivityService; import top.flya.system.utils.gaode.GaoDeMapUtil; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; /** * 活动Service业务层处理 * * @author ruoyi * @date 2023-06-02 */ @RequiredArgsConstructor @Service @Slf4j public class PzcActivityServiceImpl implements IPzcActivityService { private final PzcActivityMapper baseMapper; private final PzcIntroMapper pzcIntroMapper; private final PzcActivityConnIntroMapper pzcActivityConnIntroMapper; private final PzcArtistMapper pzcArtistMapper; private final PzcActivityConnArtistMapper pzcActivityConnArtistMapper; private final PzcTagMapper pzcTagMapper; private final PzcActivityConnTagMapper pzcActivityConnTagMapper; private final PzcOrganizerMapper pzcOrganizerMapper; private final BatchUtils batchUtils; private final PzcOrganizerTicketMapper pzcOrganizerTicketMapper; private final GaoDeMapUtil gaoDeMapUtil; /** * 查询活动 */ @Override public PzcActivityVo queryById(Integer activityId) { PzcActivityVo pzcActivityVo = baseMapper.selectVoById(activityId); if(pzcActivityVo==null) { throw new RuntimeException("当前活动不存在或者已过期,请重新选择活动哦~"); } List pzcActivityVos = new ArrayList<>(); pzcActivityVos.add(pzcActivityVo); pzcActivityVos.forEach(r->{ r.setInnerImage(pzcActivityVo.getInnerImage().contains("http")?pzcActivityVo.getInnerImage():batchUtils.getNewImageUrls(Collections.singletonList(pzcActivityVo.getInnerImage())).get(Long.parseLong(pzcActivityVo.getInnerImage()))); r.setCoverImage(pzcActivityVo.getCoverImage().contains("http")?pzcActivityVo.getCoverImage():batchUtils.getNewImageUrls(Collections.singletonList(pzcActivityVo.getCoverImage())).get(Long.parseLong(pzcActivityVo.getCoverImage()))); r.setShareImage(pzcActivityVo.getShareImage()==null?null:pzcActivityVo.getShareImage().contains("http")?pzcActivityVo.getShareImage():batchUtils.getNewImageUrls(Collections.singletonList(pzcActivityVo.getShareImage())).get(Long.parseLong(pzcActivityVo.getShareImage()))); log.info("pzcActivityVo.getInnerImage() = {}",pzcActivityVo.getInnerImage()); ArrayList introList = new ArrayList<>(); ArrayList artistList = new ArrayList<>(); ArrayList tagList = new ArrayList<>(); pzcActivityConnIntroMapper.selectList(Wrappers.lambdaQuery().eq(PzcActivityConnIntro::getActivityId, r.getActivityId())).forEach(c->{ PzcIntro pzcIntro = pzcIntroMapper.selectById(c.getIntroId()); if(pzcIntro == null){ return; } if(StringUtils.isEmpty(pzcIntro.getImageFullUrl())) { introList.add(pzcIntro); return; } Map newImageUrls = batchUtils.getNewImageUrls(Collections.singletonList(pzcIntro.getImageFullUrl())); pzcIntro.setImageFullUrl(pzcIntro.getImageFullUrl().contains("http")?pzcIntro.getImageFullUrl():newImageUrls.get(Long.parseLong(pzcIntro.getImageFullUrl()))); introList.add(pzcIntro); }); //筛选introList 中元素 PzcIntro 的type==1 r.setIntroList(introList.stream().filter(intro -> intro.getType() == 1).collect(Collectors.toList())); r.setStageList(introList.stream().filter(intro -> intro.getType() == 0).collect(Collectors.toList())); pzcActivityConnArtistMapper.selectList(Wrappers.lambdaQuery().eq(PzcActivityConnArtist::getActivityId, r.getActivityId())).forEach(c->{ PzcArtist pzcArtist = pzcArtistMapper.selectById(c.getArtistId()); if(pzcArtist == null){ return; } if(StringUtils.isEmpty(pzcArtist.getImageUrl())) { artistList.add(pzcArtist); return; } Map newImageUrls = batchUtils.getNewImageUrls(Collections.singletonList(pzcArtist.getImageUrl())); pzcArtist.setImageUrl(pzcArtist.getImageUrl().contains("http")?pzcArtist.getImageUrl():newImageUrls.get(Long.parseLong(pzcArtist.getImageUrl()))); artistList.add(pzcArtist); }); r.setArtistList(artistList); pzcActivityConnTagMapper.selectVoList(Wrappers.lambdaQuery().eq(PzcActivityConnTag::getActivityId, r.getActivityId())).forEach(c->{ PzcTag pzcTag = pzcTagMapper.selectById(c.getTagId()); if(pzcTag == null){ return; } if(StringUtils.isEmpty(pzcTag.getImageUrl())) { tagList.add(pzcTag); return; } Map newImageUrls = batchUtils.getNewImageUrls(Collections.singletonList(pzcTag.getImageUrl())); pzcTag.setImageUrl(pzcTag.getImageUrl().contains("http")?pzcTag.getImageUrl():newImageUrls.get(Long.parseLong(pzcTag.getImageUrl()))); tagList.add(pzcTag); }); r.setTagList(tagList); PzcOrganizer pzcOrganizer = pzcOrganizerMapper.selectOne(Wrappers.lambdaQuery().eq(PzcOrganizer::getOrganizerId, r.getOrganizerId())); pzcOrganizer.setLogo(pzcOrganizer.getLogo().contains("http")?pzcOrganizer.getLogo():batchUtils.getNewImageUrls(Collections.singletonList(pzcOrganizer.getLogo())).get(Long.parseLong(pzcOrganizer.getLogo()))); List pzcOrganizerTickets = pzcOrganizerTicketMapper.selectList(Wrappers.lambdaQuery().eq(PzcOrganizerTicket::getOrganizerId, pzcOrganizer.getOrganizerId())); pzcOrganizerTickets.forEach( p->{ p.setLogoImage(p.getLogoImage().contains("http")?p.getLogoImage():batchUtils.getNewImageUrls(Collections.singletonList(p.getLogoImage())).get(Long.parseLong(p.getLogoImage()))); p.setQrImage(p.getQrImage().contains("http")?p.getQrImage():batchUtils.getNewImageUrls(Collections.singletonList(p.getQrImage())).get(Long.parseLong(p.getQrImage()))); } ); pzcOrganizer.setOrganizerTickets(pzcOrganizerTickets); r.setOrganizerList(pzcOrganizer); // r.setDistance(); }); return pzcActivityVos.get(0); } /** * 查询活动列表 */ @Override public TableDataInfo queryPageList(PzcActivityBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcActivityVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询活动列表 */ @Override public TableDataInfo queryPageListWx(PzcActivityBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); pageQuery.setIsAsc("asc"); pageQuery.setOrderByColumn("start_time"); lqw.ge(PzcActivity::getShowTime, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcActivityVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询活动列表 */ @Override public List queryList(PzcActivityBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcActivityBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getAddress()), PzcActivity::getAddress, bo.getAddress()); lqw.eq(bo.getRegionId() != null, PzcActivity::getRegionId, bo.getRegionId()); lqw.like(StringUtils.isNotBlank(bo.getTitle()), PzcActivity::getTitle, bo.getTitle()); lqw.eq(StringUtils.isNotBlank(bo.getStartTime()), PzcActivity::getStartTime, bo.getStartTime()); lqw.eq(StringUtils.isNotBlank(bo.getEndDate()), PzcActivity::getEndDate, bo.getEndDate()); lqw.eq(bo.getInnerImage() != null, PzcActivity::getInnerImage, bo.getInnerImage()); lqw.eq(StringUtils.isNotBlank(bo.getShowTime()), PzcActivity::getShowTime, bo.getShowTime()); lqw.eq(StringUtils.isNotBlank(bo.getCoverImage()), PzcActivity::getCoverImage, bo.getCoverImage()); lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, PzcActivity::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); lqw.between(params.get("beginUpdateTime") != null && params.get("endUpdateTime") != null, PzcActivity::getUpdateTime, params.get("beginUpdateTime"), params.get("endUpdateTime")); lqw.eq(bo.getState() != null, PzcActivity::getState, bo.getState()); if(bo.getOrganizerList()!=null) { lqw.eq(bo.getOrganizerList().getOrganizerId() != null, PzcActivity::getOrganizerId, bo.getOrganizerList().getOrganizerId()); } if(bo.getClassify()!=null) { lqw.eq(true, PzcActivity::getClassify, bo.getClassify()); } lqw.eq(bo.getRegion()!=null, PzcActivity::getRegion, bo.getRegion()); return lqw; } /** * 新增活动 */ @Override @Transactional public Boolean insertByBo(PzcActivityBo bo) { PzcActivity add = BeanUtil.toBean(bo, PzcActivity.class); if (bo.getActivityId() != null) { throw new RuntimeException("活动id在创建时不能填写"); } log.info("新增活动 主办方Id为: {}", bo.getOrganizerList().getOrganizerId()); if(bo.getOrganizerList().getOrganizerId()==null) { //就根据输入的内容来新建主办方 PzcOrganizer organizer = new PzcOrganizer(); organizer.setName(bo.getOrganizerList().getName()); organizer.setPhone(bo.getOrganizerList().getPhone()); organizer.setContent(bo.getOrganizerList().getContent()); organizer.setLogo(bo.getOrganizerList().getLogo()); pzcOrganizerMapper.insert(organizer); log.info("新增活动 新建主办方Id为: {}", organizer.getOrganizerId()); bo.getOrganizerList().setOrganizerId(organizer.getOrganizerId()); } add.setOrganizerId(bo.getOrganizerList().getOrganizerId()); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setActivityId(add.getActivityId()); } saveActivityConfigs(bo); return flag; } @Transactional //这里关联其他表的保存 public void saveActivityConfigs(PzcActivityBo bo) { if(bo.getOrganizerList()!=null) //主办方票务 { List organizerTickets = bo.getOrganizerList().getOrganizerTickets(); if(organizerTickets!=null&&organizerTickets.size()!=0) { List ids = new ArrayList<>(); //校验PzcOrganizerTicket 的 "organizerId": 是否 是当前组织下的 organizerTickets.forEach(o->{ if(o.getOrganizerTicketId()==null) { //新建 PzcOrganizerTicket pzcOrganizerTicket = new PzcOrganizerTicket(); pzcOrganizerTicket.setOrganizerId(bo.getOrganizerList().getOrganizerId()); pzcOrganizerTicket.setName(o.getName()); pzcOrganizerTicket.setLogoImage(o.getLogoImage()); pzcOrganizerTicket.setQrImage(o.getQrImage()); pzcOrganizerTicketMapper.insert(pzcOrganizerTicket); o.setOrganizerTicketId(pzcOrganizerTicket.getOrganizerTicketId()); o.setOrganizerId(pzcOrganizerTicket.getOrganizerId()); ids.add(Math.toIntExact(o.getOrganizerTicketId())); return; } ids.add(Math.toIntExact(o.getOrganizerTicketId())); PzcOrganizerTicket pzcOrganizerTicket = pzcOrganizerTicketMapper.selectById(o.getOrganizerTicketId()); pzcOrganizerTicketMapper.update(o, Wrappers.lambdaQuery().eq(PzcOrganizerTicket::getOrganizerTicketId,o.getOrganizerTicketId())); if(!Objects.equals(bo.getOrganizerList().getOrganizerId(),pzcOrganizerTicket.getOrganizerId())) { log.info("bo.getOrganizerList().getOrganizerId() is {} , pzcOrganizerTicket.getOrganizerId() is {}",bo.getOrganizerList().getOrganizerId(),pzcOrganizerTicket.getOrganizerId()); throw new RuntimeException("票务组织者id不是当前组织者id"); } }); //删除 if(ids.size()>0) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcOrganizerTicket::getOrganizerId,bo.getOrganizerList().getOrganizerId()); lqw.notIn(PzcOrganizerTicket::getOrganizerTicketId,ids); pzcOrganizerTicketMapper.delete(lqw); } } } if(bo.getStageList().size()!=0) //舞台介绍 { bo.getStageList().forEach(stage-> { if(stage.getIntroId()==null) { //新建 PzcIntro pzcIntro = new PzcIntro(); pzcIntro.setContent(stage.getContent()); pzcIntro.setImageFullUrl(stage.getImageFullUrl()); pzcIntro.setType(stage.getType()); pzcIntro.setTitle(stage.getTitle()); pzcIntroMapper.insert(pzcIntro); stage.setIntroId(pzcIntro.getIntroId()); } //首先查询这个介绍是否存在 LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcIntro::getIntroId, stage.getIntroId()); PzcIntro pzcIntro = pzcIntroMapper.selectOne(lqw); if (pzcIntro == null) { throw new RuntimeException("舞台介绍不存在 id is " + stage.getIntroId()); } LambdaQueryWrapper lqw3 = Wrappers.lambdaQuery(); lqw3.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId()); List pzcActivityConnIntros = pzcActivityConnIntroMapper.selectList(lqw3); if(pzcActivityConnIntros.size()!=0) { pzcActivityConnIntros.forEach( p->{ Integer introId = p.getIntroId(); PzcIntro pzcIntro1 = pzcIntroMapper.selectById(introId); if(pzcIntro1==null) { return; } if(pzcIntro1.getType()==0) { pzcActivityConnIntroMapper.delete(new QueryWrapper().eq("intro_id",introId).eq("activity_id",bo.getActivityId())); //删除之后重新添加 } } ); } //介绍表关联活动表 先查询关联关系是否存在 LambdaQueryWrapper lqw2 = Wrappers.lambdaQuery(); lqw2.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId()); lqw2.eq(PzcActivityConnIntro::getIntroId, stage.getIntroId()); PzcActivityConnIntro pzcActivityConnIntro1 = pzcActivityConnIntroMapper.selectOne(lqw2); if (pzcActivityConnIntro1 != null) { return; } PzcActivityConnIntro pzcActivityConnIntro = new PzcActivityConnIntro(); pzcActivityConnIntro.setActivityId(bo.getActivityId()); pzcActivityConnIntro.setIntroId(Math.toIntExact(stage.getIntroId())); pzcActivityConnIntroMapper.insert(pzcActivityConnIntro); }); } if (bo.getIntroList().size() != 0) { //介绍 bo.getIntroList().forEach(intro -> { if(intro.getIntroId()==null) { //新建 PzcIntro pzcIntro = new PzcIntro(); pzcIntro.setContent(intro.getContent()); pzcIntro.setImageFullUrl(intro.getImageFullUrl()); pzcIntro.setType(intro.getType()); pzcIntro.setTitle(intro.getTitle()); pzcIntroMapper.insert(pzcIntro); intro.setIntroId(pzcIntro.getIntroId()); } //首先查询这个介绍是否存在 LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcIntro::getIntroId, intro.getIntroId()); PzcIntro pzcIntro = pzcIntroMapper.selectOne(lqw); if (pzcIntro == null) { throw new RuntimeException("介绍不存在 id is " + intro.getIntroId()); } LambdaQueryWrapper lqw3 = Wrappers.lambdaQuery(); lqw3.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId()); List pzcActivityConnIntros = pzcActivityConnIntroMapper.selectList(lqw3); if(pzcActivityConnIntros.size()!=0) { pzcActivityConnIntros.forEach( p->{ Integer introId = p.getIntroId(); PzcIntro pzcIntro1 = pzcIntroMapper.selectById(introId); if(pzcIntro1==null) { return; } if(pzcIntro1.getType()==1) { pzcActivityConnIntroMapper.delete(new QueryWrapper().eq("intro_id",introId).eq("activity_id",bo.getActivityId())); //删除之后重新添加 } } ); } //介绍表关联活动表 先查询关联关系是否存在 LambdaQueryWrapper lqw2 = Wrappers.lambdaQuery(); lqw2.eq(PzcActivityConnIntro::getActivityId, bo.getActivityId()); lqw2.eq(PzcActivityConnIntro::getIntroId, intro.getIntroId()); PzcActivityConnIntro pzcActivityConnIntro1 = pzcActivityConnIntroMapper.selectOne(lqw2); if (pzcActivityConnIntro1 != null) { return; } PzcActivityConnIntro pzcActivityConnIntro = new PzcActivityConnIntro(); pzcActivityConnIntro.setActivityId(bo.getActivityId()); pzcActivityConnIntro.setIntroId(Math.toIntExact(intro.getIntroId())); pzcActivityConnIntroMapper.insert(pzcActivityConnIntro); }); } if (bo.getArtistList().size() != 0) { //艺人 List artistIds = new ArrayList<>(); bo.getArtistList().forEach(artist -> { artistIds.add(Math.toIntExact(artist.getArtistId())); if(artist.getArtistId()==null) { //新建 PzcArtist pzcArtist = new PzcArtist(); pzcArtist.setName(artist.getName()); pzcArtist.setImageUrl(artist.getImageUrl()); pzcArtist.setDescription(artist.getDescription()); pzcArtistMapper.insert(pzcArtist); artist.setArtistId(pzcArtist.getArtistId()); } //首先查询这个艺人是否存在 LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcArtist::getArtistId, artist.getArtistId()); PzcArtist pzcArtist = pzcArtistMapper.selectOne(lqw); if (pzcArtist == null) { throw new RuntimeException("艺术家不存在 id is " + artist.getArtistId()); } //介绍表关联活动表 先查询关联关系是否存在 LambdaQueryWrapper lqw2 = Wrappers.lambdaQuery(); lqw2.eq(PzcActivityConnArtist::getActivityId, bo.getActivityId()); lqw2.eq(PzcActivityConnArtist::getArtistId, artist.getArtistId()); PzcActivityConnArtist pzcActivityConnArtist1 = pzcActivityConnArtistMapper.selectOne(lqw2); if (pzcActivityConnArtist1 != null) { return; } PzcActivityConnArtist pzcActivityConnArtist = new PzcActivityConnArtist(); pzcActivityConnArtist.setActivityId(bo.getActivityId()); pzcActivityConnArtist.setArtistId(Math.toIntExact(artist.getArtistId())); pzcActivityConnArtistMapper.insert(pzcActivityConnArtist); }); if(artistIds.size()>0) { //这里删除多余的 LambdaQueryWrapper lqw3 = Wrappers.lambdaQuery(); lqw3.eq(PzcActivityConnArtist::getActivityId, bo.getActivityId()); lqw3.notIn(PzcActivityConnArtist::getArtistId, artistIds); pzcActivityConnArtistMapper.delete(lqw3); } } if (bo.getTagList().size() != 0) { //标签 List tagIds = new ArrayList<>(); bo.getTagList().forEach(tag -> { tagIds.add(Math.toIntExact(tag.getTagId())); if(tag.getTagId()==null) { //新建 PzcTag pzcTag = new PzcTag(); pzcTag.setName(tag.getName()); pzcTag.setImageUrl(tag.getImageUrl()); pzcTagMapper.insert(pzcTag); tag.setTagId(pzcTag.getTagId()); } //首先查询这个标签是否存在 LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcTag::getTagId, tag.getTagId()); PzcTag pzcTag = pzcTagMapper.selectOne(lqw); if (pzcTag == null) { throw new RuntimeException("标签不存在 id is " + tag.getTagId()); } //介绍表关联活动表 先查询关联关系是否存在 LambdaQueryWrapper lqw2 = Wrappers.lambdaQuery(); lqw2.eq(PzcActivityConnTag::getActivityId, bo.getActivityId()); lqw2.eq(PzcActivityConnTag::getTagId, tag.getTagId()); PzcActivityConnTag pzcActivityConnTag1 = pzcActivityConnTagMapper.selectOne(lqw2); if (pzcActivityConnTag1 != null) { // throw new RuntimeException("标签已经关联 id is " + tag.getTagId() + "无需重复关联"); return; } PzcActivityConnTag pzcActivityConnTag = new PzcActivityConnTag(); pzcActivityConnTag.setActivityId(bo.getActivityId()); pzcActivityConnTag.setTagId(Math.toIntExact(tag.getTagId())); pzcActivityConnTagMapper.insert(pzcActivityConnTag); }); if(tagIds.size()>0) { //这里删除多余的 LambdaQueryWrapper lqw3 = Wrappers.lambdaQuery(); lqw3.eq(PzcActivityConnTag::getActivityId, bo.getActivityId()); lqw3.notIn(PzcActivityConnTag::getTagId, tagIds); pzcActivityConnTagMapper.delete(lqw3); } } } /** * 修改活动 */ @Override @Transactional public Boolean updateByBo(PzcActivityBo bo) { PzcActivity update = BeanUtil.toBean(bo, PzcActivity.class); update.setOrganizerId(bo.getOrganizerList().getOrganizerId()); validEntityBeforeSave(update); boolean flag = baseMapper.updateById(update) > 0; saveActivityConfigs(bo); return flag; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcActivity entity) { log.info("数据校验开始entity.getOrganizerId is {}", entity.getOrganizerId()); //TODO 做一些数据校验,如唯一约束 if (entity.getOrganizerId() != null) { //首先查询这个组织是否存在 LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(PzcOrganizer::getOrganizerId, entity.getOrganizerId()); PzcOrganizer pzcOrganizer = pzcOrganizerMapper.selectOne(lqw); if (pzcOrganizer == null) { throw new RuntimeException("活动组织者不存在 id is " + entity.getOrganizerId()); } } } /** * 批量删除活动 */ @Override @Transactional public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } //删除活动与其他表的关联关系 LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.in(PzcActivityConnIntro::getActivityId, ids); pzcActivityConnIntroMapper.delete(lqw); LambdaQueryWrapper lqw2 = Wrappers.lambdaQuery(); lqw2.in(PzcActivityConnArtist::getActivityId, ids); pzcActivityConnArtistMapper.delete(lqw2); LambdaQueryWrapper lqw3 = Wrappers.lambdaQuery(); lqw3.in(PzcActivityConnTag::getActivityId, ids); pzcActivityConnTagMapper.delete(lqw3); return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcArtistServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import lombok.extern.slf4j.Slf4j; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.common.BatchUtils; import top.flya.system.domain.bo.PzcArtistBo; import top.flya.system.domain.vo.PzcArtistVo; import top.flya.system.domain.PzcArtist; import top.flya.system.mapper.PzcArtistMapper; import top.flya.system.service.IPzcArtistService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 艺人Service业务层处理 * * @author flya * @date 2023-06-01 */ @RequiredArgsConstructor @Service @Slf4j public class PzcArtistServiceImpl implements IPzcArtistService { private final PzcArtistMapper baseMapper; private final BatchUtils batchUtils; /** * 查询艺人 */ @Override public PzcArtistVo queryById(Long artistId){ return baseMapper.selectVoById(artistId); } /** * 查询艺人列表 */ @Override public TableDataInfo queryPageList(PzcArtistBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcArtistVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询艺人列表 */ @Override public List queryList(PzcArtistBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcArtistBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getName()), PzcArtist::getName, bo.getName()); lqw.eq(StringUtils.isNotBlank(bo.getImageUrl()), PzcArtist::getImageUrl, bo.getImageUrl()); lqw.eq(StringUtils.isNotBlank(bo.getDescription()), PzcArtist::getDescription, bo.getDescription()); return lqw; } /** * 新增艺人 */ @Override public Boolean insertByBo(PzcArtistBo bo) { PzcArtist add = BeanUtil.toBean(bo, PzcArtist.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setArtistId(add.getArtistId()); } return flag; } /** * 修改艺人 */ @Override public Boolean updateByBo(PzcArtistBo bo) { PzcArtist update = BeanUtil.toBean(bo, PzcArtist.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcArtist entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除艺人 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcIntroServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.common.BatchUtils; import top.flya.system.domain.PzcIntro; import top.flya.system.domain.bo.PzcIntroBo; import top.flya.system.domain.vo.PzcIntroVo; import top.flya.system.mapper.PzcIntroMapper; import top.flya.system.service.IPzcIntroService; import java.util.Collection; import java.util.List; import java.util.Map; /** * 活动介绍Service业务层处理 * * @author ruoyi * @date 2023-08-04 */ @RequiredArgsConstructor @Service public class PzcIntroServiceImpl implements IPzcIntroService { private final PzcIntroMapper baseMapper; private final BatchUtils batchUtils; /** * 查询活动介绍 */ @Override public PzcIntroVo queryById(Long introId){ return baseMapper.selectVoById(introId); } /** * 查询活动介绍列表 */ @Override public TableDataInfo queryPageList(PzcIntroBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcIntroVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询活动介绍列表 */ @Override public List queryList(PzcIntroBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcIntroBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcIntro::getTitle, bo.getTitle()); lqw.eq(StringUtils.isNotBlank(bo.getContent()), PzcIntro::getContent, bo.getContent()); lqw.eq(bo.getType() != null, PzcIntro::getType, bo.getType()); lqw.eq(StringUtils.isNotBlank(bo.getImageFullUrl()), PzcIntro::getImageFullUrl, bo.getImageFullUrl()); lqw.eq(bo.getCreateTime() != null, PzcIntro::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcIntro::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增活动介绍 */ @Override public Boolean insertByBo(PzcIntroBo bo) { PzcIntro add = BeanUtil.toBean(bo, PzcIntro.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setIntroId(add.getIntroId()); } return flag; } /** * 修改活动介绍 */ @Override public Boolean updateByBo(PzcIntroBo bo) { PzcIntro update = BeanUtil.toBean(bo, PzcIntro.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcIntro entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动介绍 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOfficialServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.helper.LoginHelper; import top.flya.system.domain.PzcOfficial; import top.flya.system.domain.bo.PzcOfficialBo; import top.flya.system.domain.vo.PzcOfficialVo; import top.flya.system.mapper.PzcOfficialMapper; import top.flya.system.service.IPzcOfficialService; import java.util.Collection; import java.util.List; import java.util.Map; /** * 官方消息Service业务层处理 * * @author ruoyi * @date 2023-07-27 */ @RequiredArgsConstructor @Service public class PzcOfficialServiceImpl implements IPzcOfficialService { private final PzcOfficialMapper baseMapper; /** * 查询官方消息 */ @Override public PzcOfficialVo queryById(Long officialId){ return baseMapper.selectVoById(officialId); } /** * 查询官方消息列表 */ @Override public TableDataInfo queryPageList(PzcOfficialBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); lqw.eq(true,PzcOfficial::getIsRead,0L); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询官方消息列表 */ @Override public List queryList(PzcOfficialBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcOfficialBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getFromUserId() != null, PzcOfficial::getFromUserId, bo.getFromUserId()); lqw.in(bo.getToUserId() != null, PzcOfficial::getToUserId, bo.getToUserId(),0); //官方消息 和给我的消息 lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcOfficial::getTitle, bo.getTitle()); lqw.eq(StringUtils.isNotBlank(bo.getContent()), PzcOfficial::getContent, bo.getContent()); lqw.eq(bo.getIsRead() != null, PzcOfficial::getIsRead, bo.getIsRead()); lqw.eq(bo.getGroupId() != null, PzcOfficial::getGroupId, bo.getGroupId()); lqw.eq(bo.getActivityId() != null, PzcOfficial::getActivityId, bo.getActivityId()); lqw.eq(bo.getCreateTime() != null, PzcOfficial::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcOfficial::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增官方消息 */ @Override public Boolean insertByBo(PzcOfficialBo bo) { PzcOfficial add = BeanUtil.toBean(bo, PzcOfficial.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setOfficialId(add.getOfficialId()); } return flag; } /** * 修改官方消息 */ @Override public Boolean updateByBo(PzcOfficialBo bo) { PzcOfficial update = BeanUtil.toBean(bo, PzcOfficial.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcOfficial entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除官方消息 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } @Override public Integer read(Integer officialId) { if(officialId==null) { UpdateWrapper set = new UpdateWrapper().eq("to_user_id", LoginHelper.getUserId()).set("is_read", 1); return baseMapper.update(new PzcOfficial(),set); }else { PzcOfficial pzcOfficial = baseMapper.selectById(officialId); pzcOfficial.setIsRead(1L); return baseMapper.updateById(pzcOfficial); } } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOrderServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.domain.bo.PzcOrderBo; import top.flya.system.domain.vo.PzcOrderVo; import top.flya.system.domain.PzcOrder; import top.flya.system.mapper.PzcOrderMapper; import top.flya.system.service.IPzcOrderService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 订单Service业务层处理 * * @author ruoyi * @date 2023-07-09 */ @RequiredArgsConstructor @Service public class PzcOrderServiceImpl implements IPzcOrderService { private final PzcOrderMapper baseMapper; /** * 查询订单 */ @Override public PzcOrderVo queryById(Long orderId){ return baseMapper.selectVoById(orderId); } /** * 查询订单列表 */ @Override public TableDataInfo queryPageList(PzcOrderBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询订单列表 */ @Override public List queryList(PzcOrderBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcOrderBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getUserId() != null, PzcOrder::getUserId, bo.getUserId()); lqw.eq(bo.getActivityId() != null, PzcOrder::getActivityId, bo.getActivityId()); lqw.eq(bo.getMoney() != null, PzcOrder::getMoney, bo.getMoney()); lqw.eq(bo.getOrderStatus() != null, PzcOrder::getOrderStatus, bo.getOrderStatus()); lqw.eq(bo.getType() != null, PzcOrder::getType, bo.getType()); lqw.eq(StringUtils.isNotBlank(bo.getOutOrderNum()), PzcOrder::getOutOrderNum, bo.getOutOrderNum()); lqw.eq(StringUtils.isNotBlank(bo.getIntro()), PzcOrder::getIntro, bo.getIntro()); lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PzcOrder::getTitle, bo.getTitle()); lqw.eq(bo.getCreateTime() != null, PzcOrder::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcOrder::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增订单 */ @Override public Boolean insertByBo(PzcOrderBo bo) { PzcOrder add = BeanUtil.toBean(bo, PzcOrder.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setOrderId(add.getOrderId()); } return flag; } /** * 修改订单 */ @Override public Boolean updateByBo(PzcOrderBo bo) { PzcOrder update = BeanUtil.toBean(bo, PzcOrder.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcOrder entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除订单 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOrganizerServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.common.BatchUtils; import top.flya.system.domain.bo.PzcOrganizerBo; import top.flya.system.domain.vo.PzcOrganizerVo; import top.flya.system.domain.PzcOrganizer; import top.flya.system.mapper.PzcOrganizerMapper; import top.flya.system.service.IPzcOrganizerService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 活动主办方Service业务层处理 * * @author ruoyi * @date 2023-06-02 */ @RequiredArgsConstructor @Service public class PzcOrganizerServiceImpl implements IPzcOrganizerService { private final PzcOrganizerMapper baseMapper; private final BatchUtils batchUtils; /** * 查询活动主办方 */ @Override public PzcOrganizerVo queryById(Long organizerId){ return baseMapper.selectVoById(organizerId); } /** * 查询活动主办方列表 */ @Override public TableDataInfo queryPageList(PzcOrganizerBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcOrganizerVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询活动主办方列表 */ @Override public List queryList(PzcOrganizerBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcOrganizerBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getPhone()), PzcOrganizer::getPhone, bo.getPhone()); lqw.like(StringUtils.isNotBlank(bo.getName()), PzcOrganizer::getName, bo.getName()); lqw.eq(StringUtils.isNotBlank(bo.getLogo()), PzcOrganizer::getLogo, bo.getLogo()); lqw.eq(StringUtils.isNotBlank(bo.getContent()), PzcOrganizer::getContent, bo.getContent()); return lqw; } /** * 新增活动主办方 */ @Override public Boolean insertByBo(PzcOrganizerBo bo) { PzcOrganizer add = BeanUtil.toBean(bo, PzcOrganizer.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setOrganizerId(add.getOrganizerId()); } return flag; } /** * 修改活动主办方 */ @Override public Boolean updateByBo(PzcOrganizerBo bo) { PzcOrganizer update = BeanUtil.toBean(bo, PzcOrganizer.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcOrganizer entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动主办方 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcOrganizerTicketServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.PzcOrganizerTicket; import top.flya.system.domain.bo.PzcOrganizerTicketBo; import top.flya.system.domain.vo.PzcOrganizerTicketVo; import top.flya.system.mapper.PzcOrganizerTicketMapper; import top.flya.system.service.IPzcOrganizerTicketService; import java.util.Collection; import java.util.List; import java.util.Map; /** * 主办方票务Service业务层处理 * * @author ruoyi * @date 2023-07-22 */ @RequiredArgsConstructor @Service public class PzcOrganizerTicketServiceImpl implements IPzcOrganizerTicketService { private final PzcOrganizerTicketMapper baseMapper; /** * 查询主办方票务 */ @Override public PzcOrganizerTicketVo queryById(Long organizerTicketId){ return baseMapper.selectVoById(organizerTicketId); } /** * 查询主办方票务列表 */ @Override public TableDataInfo queryPageList(PzcOrganizerTicketBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询主办方票务列表 */ @Override public List queryList(PzcOrganizerTicketBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcOrganizerTicketBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getName()), PzcOrganizerTicket::getName, bo.getName()); lqw.eq(StringUtils.isNotBlank(bo.getQrImage()), PzcOrganizerTicket::getQrImage, bo.getQrImage()); lqw.eq(StringUtils.isNotBlank(bo.getLogoImage()), PzcOrganizerTicket::getLogoImage, bo.getLogoImage()); lqw.eq(bo.getOrganizerId() != null, PzcOrganizerTicket::getOrganizerId, bo.getOrganizerId()); return lqw; } /** * 新增主办方票务 */ @Override public Boolean insertByBo(PzcOrganizerTicketBo bo) { PzcOrganizerTicket add = BeanUtil.toBean(bo, PzcOrganizerTicket.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setOrganizerTicketId(add.getOrganizerTicketId()); } return flag; } /** * 修改主办方票务 */ @Override public Boolean updateByBo(PzcOrganizerTicketBo bo) { PzcOrganizerTicket update = BeanUtil.toBean(bo, PzcOrganizerTicket.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcOrganizerTicket entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除主办方票务 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcRegionServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.PzcRegion; import top.flya.system.domain.bo.PzcRegionBo; import top.flya.system.domain.vo.PzcRegionVo; import top.flya.system.mapper.PzcRegionMapper; import top.flya.system.service.IPzcRegionService; import java.util.Collection; import java.util.List; import java.util.Map; /** * 地区Service业务层处理 * * @author ruoyi * @date 2023-07-22 */ @RequiredArgsConstructor @Service public class PzcRegionServiceImpl implements IPzcRegionService { private final PzcRegionMapper baseMapper; /** * 查询地区 */ @Override public PzcRegionVo queryById(Long regionId){ return baseMapper.selectVoById(regionId); } /** * 查询地区列表 */ @Override public TableDataInfo queryPageList(PzcRegionBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 不分页全查 */ /** * 查询地区列表 */ @Override public List queryList(PzcRegionBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcRegionBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getBase()), PzcRegion::getBase, bo.getBase()); lqw.like(StringUtils.isNotBlank(bo.getName()), PzcRegion::getName, bo.getName()); lqw.eq(StringUtils.isNotBlank(bo.getImgUrl()), PzcRegion::getImgUrl, bo.getImgUrl()); lqw.eq(bo.getCreateTime() != null, PzcRegion::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcRegion::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增地区 */ @Override public Boolean insertByBo(PzcRegionBo bo) { PzcRegion add = BeanUtil.toBean(bo, PzcRegion.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setRegionId(add.getRegionId()); } return flag; } /** * 修改地区 */ @Override public Boolean updateByBo(PzcRegionBo bo) { PzcRegion update = BeanUtil.toBean(bo, PzcRegion.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcRegion entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除地区 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcTagServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.common.BatchUtils; import top.flya.system.domain.bo.PzcTagBo; import top.flya.system.domain.vo.PzcTagVo; import top.flya.system.domain.PzcTag; import top.flya.system.mapper.PzcTagMapper; import top.flya.system.service.IPzcTagService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 活动标签Service业务层处理 * * @author ruoyi * @date 2023-06-02 */ @RequiredArgsConstructor @Service public class PzcTagServiceImpl implements IPzcTagService { private final PzcTagMapper baseMapper; private final BatchUtils batchUtils; /** * 查询活动标签 */ @Override public PzcTagVo queryById(Long tagId){ return baseMapper.selectVoById(tagId); } /** * 查询活动标签列表 */ @Override public TableDataInfo queryPageList(PzcTagBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcTagVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询活动标签列表 */ @Override public List queryList(PzcTagBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcTagBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getName()), PzcTag::getName, bo.getName()); lqw.eq(StringUtils.isNotBlank(bo.getImageUrl()), PzcTag::getImageUrl, bo.getImageUrl()); lqw.eq(bo.getCreateTime() != null, PzcTag::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcTag::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增活动标签 */ @Override public Boolean insertByBo(PzcTagBo bo) { PzcTag add = BeanUtil.toBean(bo, PzcTag.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setTagId(add.getTagId()); } return flag; } /** * 修改活动标签 */ @Override public Boolean updateByBo(PzcTagBo bo) { PzcTag update = BeanUtil.toBean(bo, PzcTag.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcTag entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除活动标签 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserCollectServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import lombok.extern.slf4j.Slf4j; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.common.BatchUtils; import top.flya.system.domain.bo.PzcUserCollectBo; import top.flya.system.domain.vo.PzcUserCollectVo; import top.flya.system.domain.PzcUserCollect; import top.flya.system.mapper.PzcActivityMapper; import top.flya.system.mapper.PzcUserCollectMapper; import top.flya.system.service.IPzcUserCollectService; import java.util.*; /** * 用户收藏活动Service业务层处理 * * @author ruoyi * @date 2023-07-08 */ @RequiredArgsConstructor @Service @Slf4j public class PzcUserCollectServiceImpl implements IPzcUserCollectService { private final PzcUserCollectMapper baseMapper; private final PzcActivityMapper pzcActivityMapper; private final BatchUtils batchUtils; /** * 查询用户收藏活动 */ @Override public PzcUserCollectVo queryById(Long collectId){ return baseMapper.selectVoById(collectId); } /** * 查询用户收藏活动列表 */ @Override public TableDataInfo queryPageList(PzcUserCollectBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); List records = new ArrayList<>(); result.getRecords().forEach( vo -> { vo.setActivity(pzcActivityMapper.selectById(vo.getActivityId())); if(vo.getActivity()!=null) { log.info("qaq: {} vo.getActivity().getClassify() is {} bo.getType() is {}",vo.getActivity().getClassify().equals(bo.getType()),vo.getActivity().getClassify(),bo.getType()); if(vo.getActivity().getClassify().equals(bo.getType())) { log.info("vo.getActivity() is:"+vo.getActivity().getClassify()); vo.getActivity().setCoverImage(vo.getActivity().getCoverImage().contains("http")?vo.getActivity().getCoverImage(): batchUtils.getNewImageUrls(Collections.singletonList(vo.getActivity().getCoverImage())).get(Long.parseLong(vo.getActivity().getCoverImage()))); // vo.getActivity().setShareImage(vo.getActivity().getShareImage().contains("http")?vo.getActivity().getShareImage(): batchUtils.getNewImageUrls(Collections.singletonList(vo.getActivity().getShareImage())).get(Long.parseLong(vo.getActivity().getShareImage()))); records.add(vo); } } } ); return TableDataInfo.build(records); } /** * 查询用户收藏活动列表 */ @Override public List queryList(PzcUserCollectBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcUserCollectBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getUserId() != null, PzcUserCollect::getUserId, bo.getUserId()); lqw.eq(bo.getActivityId() != null, PzcUserCollect::getActivityId, bo.getActivityId()); return lqw; } /** * 新增用户收藏活动 */ @Override public Boolean insertByBo(PzcUserCollectBo bo) { PzcUserCollect add = BeanUtil.toBean(bo, PzcUserCollect.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setCollectId(add.getCollectId()); } return flag; } /** * 修改用户收藏活动 */ @Override public Boolean updateByBo(PzcUserCollectBo bo) { PzcUserCollect update = BeanUtil.toBean(bo, PzcUserCollect.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcUserCollect entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除用户收藏活动 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserHistoryServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.common.utils.DateUtils; import top.flya.system.domain.bo.PzcUserHistoryBo; import top.flya.system.domain.vo.PzcUserHistoryVo; import top.flya.system.domain.PzcUserHistory; import top.flya.system.mapper.PzcUserHistoryMapper; import top.flya.system.service.IPzcUserHistoryService; import java.text.ParseException; import java.util.List; import java.util.Map; import java.util.Collection; /** * 用户操作历史记录Service业务层处理 * * @author ruoyi * @date 2023-07-06 */ @RequiredArgsConstructor @Service public class PzcUserHistoryServiceImpl implements IPzcUserHistoryService { private final PzcUserHistoryMapper baseMapper; /** * 查询用户操作历史记录 */ @Override public PzcUserHistoryVo queryById(Long historyId){ return baseMapper.selectVoById(historyId); } /** * 查询用户操作历史记录列表 */ @Override public TableDataInfo queryPageList(PzcUserHistoryBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询用户操作历史记录列表 */ @Override public List queryList(PzcUserHistoryBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcUserHistoryBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getUserId() != null, PzcUserHistory::getUserId, bo.getUserId()); lqw.eq(bo.getActivityId() != null, PzcUserHistory::getActivityId, bo.getActivityId()); lqw.in(bo.getType() != null, PzcUserHistory::getType, bo.getType()); lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcUserHistory::getMessage, bo.getMessage()); //获取本月开始时间和结束时间 if(StringUtils.isNotBlank(bo.getNowTime())&&bo.getNowTime().length()>0) { try { lqw.between(true, PzcUserHistory::getCreateTime,DateUtils.getMonthStartAndEnd(bo.getNowTime())[0], DateUtils.getMonthStartAndEnd(bo.getNowTime())[1]); } catch (ParseException e) { throw new RuntimeException(e); } } return lqw; } /** * 新增用户操作历史记录 */ @Override public Boolean insertByBo(PzcUserHistoryBo bo) { PzcUserHistory add = BeanUtil.toBean(bo, PzcUserHistory.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setHistoryId(add.getHistoryId()); } return flag; } /** * 修改用户操作历史记录 */ @Override public Boolean updateByBo(PzcUserHistoryBo bo) { PzcUserHistory update = BeanUtil.toBean(bo, PzcUserHistory.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcUserHistory entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除用户操作历史记录 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserPhotoServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.domain.bo.PzcUserPhotoBo; import top.flya.system.domain.vo.PzcUserPhotoVo; import top.flya.system.domain.PzcUserPhoto; import top.flya.system.mapper.PzcUserPhotoMapper; import top.flya.system.service.IPzcUserPhotoService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 用户资料相册Service业务层处理 * * @author ruoyi * @date 2023-07-11 */ @RequiredArgsConstructor @Service public class PzcUserPhotoServiceImpl implements IPzcUserPhotoService { private final PzcUserPhotoMapper baseMapper; /** * 查询用户资料相册 */ @Override public PzcUserPhotoVo queryById(Long photoId){ return baseMapper.selectVoById(photoId); } /** * 查询用户资料相册列表 */ @Override public TableDataInfo queryPageList(PzcUserPhotoBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * 查询用户资料相册列表 */ @Override public List queryList(PzcUserPhotoBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcUserPhotoBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getUserId() != null, PzcUserPhoto::getUserId, bo.getUserId()); lqw.eq(StringUtils.isNotBlank(bo.getUrl()), PzcUserPhoto::getUrl, bo.getUrl()); lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcUserPhoto::getMessage, bo.getMessage()); lqw.eq(bo.getCreateTime() != null, PzcUserPhoto::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcUserPhoto::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增用户资料相册 */ @Override public Boolean insertByBo(PzcUserPhotoBo bo) { PzcUserPhoto add = BeanUtil.toBean(bo, PzcUserPhoto.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setPhotoId(add.getPhotoId()); } return flag; } /** * 修改用户资料相册 */ @Override public Boolean updateByBo(PzcUserPhotoBo bo) { PzcUserPhoto update = BeanUtil.toBean(bo, PzcUserPhoto.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcUserPhoto entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除用户资料相册 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.common.BatchUtils; import top.flya.system.domain.PzcUser; import top.flya.system.domain.bo.PzcUserBo; import top.flya.system.domain.bo.UpdateMoneyBo; import top.flya.system.domain.vo.PzcUserVo; import top.flya.system.mapper.PzcUserMapper; import top.flya.system.service.IPzcUserService; import java.util.Collection; import java.util.List; import java.util.Map; /** * 用户Service业务层处理 * * @author ruoyi * @date 2023-07-09 */ @RequiredArgsConstructor @Service public class PzcUserServiceImpl implements IPzcUserService { private final PzcUserMapper baseMapper; private final BatchUtils batchUtils; /** * 查询用户 */ @Override public PzcUserVo queryById(Long userId){ return baseMapper.selectVoById(userId); } /** * 查询用户列表 */ @Override public TableDataInfo queryPageList(PzcUserBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcUserVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询用户列表 */ @Override public List queryList(PzcUserBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcUserBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getOpenid()), PzcUser::getOpenid, bo.getOpenid()); lqw.eq(bo.getMoney() != null, PzcUser::getMoney, bo.getMoney()); lqw.eq(bo.getUserLevel() != null, PzcUser::getUserLevel, bo.getUserLevel()); lqw.eq(bo.getIntegration() != null, PzcUser::getIntegration, bo.getIntegration()); lqw.eq(bo.getIntegrationNow() != null, PzcUser::getIntegrationNow, bo.getIntegrationNow()); lqw.like(StringUtils.isNotBlank(bo.getRealname()), PzcUser::getRealname, bo.getRealname()); lqw.like(StringUtils.isNotBlank(bo.getNickname()), PzcUser::getNickname, bo.getNickname()); lqw.eq(bo.getSex() != null, PzcUser::getSex, bo.getSex()); lqw.eq(StringUtils.isNotBlank(bo.getPhone()), PzcUser::getPhone, bo.getPhone()); lqw.eq(StringUtils.isNotBlank(bo.getAvatar()), PzcUser::getAvatar, bo.getAvatar()); lqw.eq(StringUtils.isNotBlank(bo.getAddress()), PzcUser::getAddress, bo.getAddress()); lqw.eq(StringUtils.isNotBlank(bo.getIntro()), PzcUser::getIntro, bo.getIntro()); lqw.eq(bo.getAge() != null, PzcUser::getAge, bo.getAge()); lqw.eq(StringUtils.isNotBlank(bo.getConstellation()), PzcUser::getConstellation, bo.getConstellation()); lqw.eq(StringUtils.isNotBlank(bo.getMbti()), PzcUser::getMbti, bo.getMbti()); lqw.eq(bo.getHobby() != null, PzcUser::getHobby, bo.getHobby()); lqw.eq(StringUtils.isNotBlank(bo.getSchool()), PzcUser::getSchool, bo.getSchool()); lqw.eq(StringUtils.isNotBlank(bo.getOccupation()), PzcUser::getOccupation, bo.getOccupation()); lqw.eq(StringUtils.isNotBlank(bo.getMusicStyle()), PzcUser::getMusicStyle, bo.getMusicStyle()); lqw.eq(bo.getState() != null, PzcUser::getState, bo.getState()); return lqw; } /** * 新增用户 */ @Override public Boolean insertByBo(PzcUserBo bo) { PzcUser add = BeanUtil.toBean(bo, PzcUser.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setUserId(add.getUserId()); } return flag; } /** * 修改用户 */ @Override public Boolean updateByBo(PzcUserBo bo) { PzcUser update = BeanUtil.toBean(bo, PzcUser.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcUser entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除用户 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } @Override public int updateMoney(UpdateMoneyBo bo) { return baseMapper.updateMoney(bo); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcUserTalkServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.helper.LoginHelper; import top.flya.system.domain.PzcUser; import top.flya.system.domain.PzcUserTalk; import top.flya.system.domain.bo.PzcUserTalkBo; import top.flya.system.domain.vo.PzcUserTalkVo; import top.flya.system.mapper.PzcUserMapper; import top.flya.system.mapper.PzcUserTalkMapper; import top.flya.system.service.IPzcUserTalkService; import java.util.*; import java.util.stream.Collectors; /** * 用户聊天Service业务层处理 * * @author ruoyi * @date 2023-07-16 */ @RequiredArgsConstructor @Service @Slf4j public class PzcUserTalkServiceImpl implements IPzcUserTalkService { private final PzcUserTalkMapper baseMapper; private final PzcUserMapper pzcUserMapper; /** * 查询用户聊天 */ @Override public PzcUserTalkVo queryById(Long talkId) { return baseMapper.selectVoById(talkId); } /** * 查询用户聊天列表 */ @Override public TableDataInfo queryPageList(PzcUserTalkBo bo, PageQuery pageQuery) { Page result = baseMapper.selectVoPageV2(pageQuery.build(), bo); return TableDataInfo.build(result); } /** * 查询我 与朋友的聊天列表 * * @param bo * @param pageQuery * @return */ @Override public TableDataInfo queryMyPageList(PzcUserTalkBo bo, PageQuery pageQuery) { List userIds = baseMapper.selectMyTalkUserIds(LoginHelper.getUserId()); List userIds2 = baseMapper.selectMyTalkUserIdsV2(LoginHelper.getUserId()); List result =new ArrayList<>(); userIds.addAll(userIds2); userIds=userIds.stream().distinct().collect(Collectors.toList()); log.info("聊天列表 对方 userIds:{}", userIds); userIds.forEach( userId -> { PzcUserTalkVo item = baseMapper.selectVoPageV1(userId, LoginHelper.getUserId()); // if(item==null) // { // return; //TODO // } Long fromUserId = item.getFromUserId().equals(LoginHelper.getUserId()) ? item.getToUserId() : item.getFromUserId(); item.setNotReadCount(baseMapper.selectNotReadCount(fromUserId, LoginHelper.getUserId(),LoginHelper.getUserId())); // PzcUser pzcUser = pzcUserMapper.selectById(Objects.equals(item.getToUserId(), LoginHelper.getUserId()) ? item.getFromUserId() : item.getToUserId()); item.setUsername(pzcUser.getNickname()); item.setAvatar(pzcUser.getAvatar()); item.setToUserId(item.getToUserId().equals(LoginHelper.getUserId()) ? item.getFromUserId() : item.getToUserId()); result.add(item); } ); List collect = result.stream().sorted(Comparator.comparing(PzcUserTalkVo::getCreateTime).reversed()).collect(Collectors.toList()); return TableDataInfo.build(collect); } /** * 查询用户聊天列表 */ @Override public List queryList(PzcUserTalkBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcUserTalkBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(bo.getFromUserId() != null, PzcUserTalk::getFromUserId, bo.getFromUserId()); lqw.eq(bo.getToUserId() != null, PzcUserTalk::getToUserId, bo.getToUserId()); lqw.eq(StringUtils.isNotBlank(bo.getMessage()), PzcUserTalk::getMessage, bo.getMessage()); lqw.eq(bo.getMessageStatus() != null, PzcUserTalk::getMessageStatus, bo.getMessageStatus()); lqw.eq(bo.getMessageType() != null, PzcUserTalk::getMessageType, bo.getMessageType()); lqw.eq(bo.getCreateTime() != null, PzcUserTalk::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getUpdateTime() != null, PzcUserTalk::getUpdateTime, bo.getUpdateTime()); return lqw; } /** * 新增用户聊天 */ @Override public Boolean insertByBo(PzcUserTalkBo bo) { PzcUserTalk add = BeanUtil.toBean(bo, PzcUserTalk.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setTalkId(add.getTalkId()); } return flag; } /** * 修改用户聊天 */ @Override public Boolean updateByBo(PzcUserTalkBo bo) { PzcUserTalk update = BeanUtil.toBean(bo, PzcUserTalk.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcUserTalk entity) { //TODO 做一些数据校验,如唯一约束 } /** * 批量删除用户聊天 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/service/impl/PzcViewPagerServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.flya.system.common.BatchUtils; import top.flya.system.domain.bo.PzcViewPagerBo; import top.flya.system.domain.vo.PzcViewPagerVo; import top.flya.system.domain.PzcViewPager; import top.flya.system.mapper.PzcViewPagerMapper; import top.flya.system.service.IPzcViewPagerService; import java.util.List; import java.util.Map; import java.util.Collection; /** * 轮播图Service业务层处理 * * @author ruoyi * @date 2023-05-23 */ @RequiredArgsConstructor @Service public class PzcViewPagerServiceImpl implements IPzcViewPagerService { private final PzcViewPagerMapper baseMapper; private final BatchUtils batchUtils; /** * 查询轮播图 */ @Override public PzcViewPagerVo queryById(Integer viewPagerId){ return baseMapper.selectVoById(viewPagerId); } /** * 查询轮播图列表 */ @Override public TableDataInfo queryPageList(PzcViewPagerBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); result.setRecords(batchUtils.transformToPzcViewPagerVo(result.getRecords())); return TableDataInfo.build(result); } /** * 查询轮播图列表 */ @Override public List queryList(PzcViewPagerBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper buildQueryWrapper(PzcViewPagerBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getName()), PzcViewPager::getName, bo.getName()); lqw.eq(StringUtils.isNotBlank(bo.getImageUrl()), PzcViewPager::getImageUrl, bo.getImageUrl()); lqw.eq(StringUtils.isNotBlank(bo.getLinkUrl()), PzcViewPager::getLinkUrl, bo.getLinkUrl()); lqw.eq(bo.getState() != null, PzcViewPager::getState, bo.getState()); lqw.eq(bo.getActivityId() != null, PzcViewPager::getActivityId, bo.getActivityId()); return lqw; } /** * 新增轮播图 */ @Override public Boolean insertByBo(PzcViewPagerBo bo) { PzcViewPager add = BeanUtil.toBean(bo, PzcViewPager.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setViewPagerId(add.getViewPagerId()); } return flag; } /** * 修改轮播图 */ @Override public Boolean updateByBo(PzcViewPagerBo bo) { PzcViewPager update = BeanUtil.toBean(bo, PzcViewPager.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(PzcViewPager entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除轮播图 */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/ActivityUtils.java ================================================ package top.flya.system.utils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.DateUtils; import top.flya.system.domain.vo.PzcActivityGroupVo; import top.flya.system.domain.vo.PzcActivityVo; import top.flya.system.mapper.PzcActivityGroupMapper; import top.flya.system.mapper.PzcActivityMapper; import top.flya.system.service.IPzcActivityGroupService; import top.flya.system.service.IPzcActivityService; import java.util.Date; @Component @Slf4j @RequiredArgsConstructor public class ActivityUtils { private final IPzcActivityService iPzcActivityService; private final PzcActivityMapper pzcActivityMapper; private final IPzcActivityGroupService iPzcActivityGroupService; private final PzcActivityGroupMapper pzcActivityGroupMapper; /** * 检查活动相关问题 活动是否存在 活动是否结束 * @param activityId * @return */ public Boolean checkActivity(Integer activityId) { log.info("checkActivity: activityId = {}", activityId); if(activityId==0) { return true;//城市活动 无需检查 } PzcActivityVo pzcActivityVo = iPzcActivityService.queryById(activityId); if (pzcActivityVo == null) { log.error("活动不存在"); throw new RuntimeException("活动不存在"); } String endDate = pzcActivityVo.getEndDate(); Date now = new Date(); Date end = DateUtils.parseDate(endDate); if(now.after(end)) { log.error("活动已结束"); throw new RuntimeException("活动已结束"); } return true; } /** * 检查活动组是否存在 和当前组是否在当前活动下 * @param groupId * @return */ public Boolean checkGroup(Integer activityId,Long groupId) { log.info("checkGroup: groupId = {}", groupId); PzcActivityGroupVo pzcActivityGroupVo = iPzcActivityGroupService.queryById(groupId); if(pzcActivityGroupVo == null) { log.error("组队不存在"); throw new RuntimeException("组队不存在"); } //不可以参与自己发起的组队 if(pzcActivityGroupVo.getUserId().equals(LoginHelper.getUserId())) { log.error("不可以参与自己发起的组队"); throw new RuntimeException("不可以参与自己发起的组队"); } Long activityId1 = pzcActivityGroupVo.getActivityId(); if(activityId.equals(Math.toIntExact(activityId1))) { return true; }else { log.error("组队不在当前活动下"); throw new RuntimeException("组队不在当前活动下"); } } public Boolean allCheck(Integer activityId, Long groupId) { return checkActivity(activityId) && checkGroup(activityId,groupId); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/CreateSign.java ================================================ package top.flya.system.utils; /** * Created with IntelliJ IDEA. * * @author: 风离 * @Date: 2022/06/04/14:06 * @Description: */ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.security.Signature; import java.util.Base64; @Slf4j @Component public class CreateSign { /** * 作用:使用字段appId、timeStamp、nonceStr、package计算得出的签名值 * 场景:根据微信统一下单接口返回的 prepay_id 生成调启支付所需的签名值 * @param appId * @param timestamp * @param nonceStr * @param pack package * @return * @throws Exception */ public String getSign(String appId, long timestamp, String nonceStr, String pack) throws Exception{ String message = buildMessage(appId, timestamp, nonceStr, pack); log.info("message: \n"+message); log.info("======end======"); String paySign= sign(message.getBytes("utf-8")); return paySign; } private String buildMessage(String appId, long timestamp, String nonceStr, String pack) { return appId + "\n" + timestamp + "\n" + nonceStr + "\n" + pack + "\n"; } private String sign(byte[] message) throws Exception{ Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(MyPrivateKey.getPrivateKey("apiclient_key.pem")); sign.update(message); return Base64.getEncoder().encodeToString(sign.sign()); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/MyPrivateKey.java ================================================ package top.flya.system.utils; /** * Created with IntelliJ IDEA. * * @author: 风离 * @Date: 2022/06/04/14:57 * @Description: */ import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; import org.springframework.core.io.ClassPathResource; import java.io.IOException; import java.security.PrivateKey; public class MyPrivateKey { /** * 获取私钥。 * 这是个静态方法,可以直接用类名调用 * @param filename 私钥文件路径 (required) * @return 私钥对象 * * 完全不需要修改,注意此方法也是去掉了头部和尾部,注意文件路径名 */ public static PrivateKey getPrivateKey(String filename) throws IOException { ClassPathResource resource = new ClassPathResource(filename); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey( resource.getInputStream()); return merchantPrivateKey; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/WxUtils.java ================================================ package top.flya.system.utils; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import top.flya.common.core.domain.R; import top.flya.common.helper.LoginHelper; import top.flya.system.domain.PzcOfficial; import top.flya.system.domain.PzcUser; import top.flya.system.domain.PzcUserHistory; import top.flya.system.domain.vo.PzcActivityGroupApplyVo; import top.flya.system.mapper.PzcOfficialMapper; import top.flya.system.mapper.PzcUserHistoryMapper; import top.flya.system.mapper.PzcUserMapper; import top.flya.system.service.IPzcActivityGroupApplyService; import top.flya.system.utils.sensitivewordsfiliter.WorldsFilterUtils; import javax.annotation.Resource; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; @Component @Slf4j public class WxUtils { @Value("${wx.appId}") private String appId; @Value("${wx.appSecret}") private String secret; @Resource private PzcUserMapper userMapper; @Resource private PzcUserHistoryMapper userHistoryMapper; @Resource private IPzcActivityGroupApplyService iPzcActivityGroupApplyService; @Resource private PzcOfficialMapper pzcOfficialMapper; public void checkMgc(String msg) { if(msg==null|| msg.isEmpty()) { return; } if(WorldsFilterUtils.checkBySystemWords(msg)) { throw new RuntimeException("输入内容包含敏感词汇,请重新输入"); } } public R sendArriveMsg(String toUserOpenId, Map data) { String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?" + "grant_type=client_credential&appid=" + appId + "&secret=" + secret; String response = HttpUtil.get(getTokenUrl); log.info("微信小程序获取token url : {},response is {}", getTokenUrl, response); JSONObject wxUser = JSONObject.parseObject(response); if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get("errcode") != null) { throw new RuntimeException("微信登录失败 可能是code过期了"); } String accessToken = wxUser.get("access_token").toString(); String msgUrl= "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="+accessToken; Map map = new HashMap<>(); map.put("template_id","MMHCiz9Z5faTwbDI9ywE0ScIvGMeDduTxXm00wdLxmw"); map.put("touser",toUserOpenId); map.put("data",data); map.put("miniprogram_state","formal");//developer为开发版;trial为体验版;formal为正式版;默认为正式版 map.put("lang","zh_CN"); log.info("request is {}",JSONUtil.toJsonStr(map)); String msgResponse = HttpUtil.post(msgUrl, JSONUtil.toJsonStr(map)); log.info("response is {}",msgResponse); JSONObject msgJson = JSONObject.parseObject(msgResponse); if (msgJson.getInteger("errcode") != 0) { throw new RuntimeException("微信小程序推送消息失败"); } return R.ok(msgJson.get("errcode").toString()); } public PzcActivityGroupApplyVo checkApplyConfirm(Long applyId) { //首先判断 这个applyId 的状态 以及是否存在 PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId); if(pzcActivityGroupApplyVo==null) { throw new RuntimeException("申请不存在"); // return R.fail("申请不存在"); } Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus(); if(applyStatus==-1||applyStatus==0||applyStatus==3) { throw new RuntimeException("该订单位于【"+applyStatus(applyStatus)+"】状态,不可确认"); } return pzcActivityGroupApplyVo; } public PzcActivityGroupApplyVo checkApplyPj(long applyId) { //首先判断 这个applyId 的状态 以及是否存在 PzcActivityGroupApplyVo pzcActivityGroupApplyVo = iPzcActivityGroupApplyService.queryById(applyId); if(pzcActivityGroupApplyVo==null) { throw new RuntimeException("申请不存在"); } Integer applyStatus = pzcActivityGroupApplyVo.getApplyStatus(); //-1 已取消 0 位于申请列表中 1 申请通过待确认时 //2 确认通过进行中 3 组队结束 9发起方已确认 // 10申请方已确认 11 发起方已打卡 12 申请方已打卡 //13 申请方已评价 14 发起方已评价 15 双方已评价 if(applyStatus==3||applyStatus==13||applyStatus==14) { return pzcActivityGroupApplyVo; }else { throw new RuntimeException("该订单位于【"+applyStatus(applyStatus)+"】状态,不可评价"); } } public String applyStatus(Integer applyStatus) { if(applyStatus==-1) { return "已取消"; } if(applyStatus==0) { return "位于申请列表中"; } if(applyStatus==1) { return "申请通过待确认"; } if(applyStatus==2) { return "已确认,进行中"; } if(applyStatus==3) { return "已完成"; } if(applyStatus==9) { return "发起方已确认"; } if(applyStatus==10) { return "申请方已确认"; } if(applyStatus==11) { return "发起方已打卡"; } if (applyStatus==12) { return "申请方已打卡"; } if (applyStatus==13) { return "发起方已评价"; } if (applyStatus==14) { return "申请方已评价"; } if (applyStatus==15) { return "双方已评价"; } return null; } public String getAccessToken() { String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + secret; String result = HttpUtil.get(url); return JSONUtil.parseObj(result).getStr("access_token"); } public PzcUser checkUser() { Long userId = LoginHelper.getUserId(); PzcUser user = userMapper.selectById(userId); if (user == null || StringUtils.isEmpty(user.getOpenid())|| user.getState() == 0) { throw new RuntimeException("用户不存在 或者已被禁用"); } return user; } public void insertPzcOfficialMsg(Long fromUserId,Long toUserId,String title,String content,Long groupId,Long activityId) { PzcOfficial pzcOfficial = new PzcOfficial(); pzcOfficial.setIsRead(0L); pzcOfficial.setFromUserId(fromUserId); pzcOfficial.setTitle(title); pzcOfficial.setContent(content); pzcOfficial.setToUserId(toUserId); pzcOfficial.setGroupId(groupId); pzcOfficial.setActivityId(activityId); int insert = pzcOfficialMapper.insert(pzcOfficial); log.info("插入官方消息条数:{}\n内容为:{}", insert,JSONUtil.toJsonPrettyStr(pzcOfficial)); } public void insertUserHistory(Long userId, Long activityId, Long type, String message, BigDecimal money) { PzcUserHistory userHistory = new PzcUserHistory(); userHistory.setUserId(userId); userHistory.setActivityId(activityId); userHistory.setType(type); userHistory.setMessage(message); userHistory.setMoney(money); int insert = userHistoryMapper.insert(userHistory); log.info("插入用户历史记录 信息为: {} 条数为: {}",message,insert); } public void checkApplyScore(Integer score) { if(score!=0&&score!=3&&score!=-3) { throw new RuntimeException("评分只能为差评(-3 积分) 中评(+0 积分) 好评(+3 积分)"); } } public void updateUserMsg(PzcUser otherUser) { if(otherUser.getIntegrationNow()>=30) { otherUser.setUserLevel(2L); } if(otherUser.getIntegrationNow()>=70) { otherUser.setUserLevel(3L); } if(otherUser.getIntegrationNow()>=150) { otherUser.setUserLevel(4L); } if(otherUser.getIntegrationNow()>=300) { otherUser.setUserLevel(5L); } } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/gaode/GaoDeEnum.java ================================================ package top.flya.system.utils.gaode; import lombok.AllArgsConstructor; import lombok.Getter; /** * @Description: 高德地图枚举类 * @Author: isymikasan * @Date: 2022-01-26 09:36:55 */ @AllArgsConstructor @Getter public enum GaoDeEnum { // 高德地图固定字段 STATUS("status"), INT_ONE("1"), RE_GEO_CODE("regeocode"), GEO_CODES("geocodes"), LOCATION("location"), FORMATTED_ADDRESS("formatted_address"), RESULTS("results"), DISTANCE("distance"); private String code; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/gaode/GaoDeMapUtil.java ================================================ package top.flya.system.utils.gaode; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import top.flya.common.core.domain.R; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; /** * @Description: 高德地图工具类 * @Author: isymikasan * @Date: 2021-12-22 09:19:02 */ @Component @Slf4j public class GaoDeMapUtil { /** * 功能描述: 高德地图Key * * @param null * @return * @author 周兆宇 * @date 2022-01-26 09:13:40 */ private static final String GAO_DE_KEY = "112049d76fe83e408d4ecceafb2ad4e3"; //申请的账户Key /** * 功能描述: 根据地址名称得到两个地址间的距离 * * @param start 起始位置 * @param end 结束位置 * @return long 两个地址间的距离 * @author isymikasan * @date 2022-01-26 09:16:04 */ public Long getDistanceByAddress(String start, String end) { String startLonLat = getLonLat(start).getData().toString(); String endLonLat = getLonLat(end).getData().toString(); Long distance = Long.valueOf(getDistance(startLonLat, endLonLat).getData().toString()); return distance; } /** * 功能描述: 地址转换为经纬度 * * @param address 地址 * @return java.lang.String 经纬度 * @author isymikasan * @date 2022-01-26 09:17:13 */ public R getLonLat(String address) { log.info("地址为:" + address); // 返回输入地址address的经纬度信息, 格式是 经度,纬度 String queryUrl = "http://restapi.amap.com/v3/geocode/geo?key=" + GAO_DE_KEY + "&address=" + address; // 高德接口返回的是JSON格式的字符串 String queryResult = getResponse(queryUrl); JSONObject job = JSONObject.parseObject(queryResult); log.info("高德接口返回的是JSON格式的字符串:" + queryResult); JSONObject jobJSON = JSONObject .parseObject( job.get("geocodes").toString().substring(1, job.get("geocodes").toString().length() - 1)); String LngAndLat = jobJSON.get("location").toString(); log.info("经纬度为:" + LngAndLat); return R.ok("经纬度转换成功!",LngAndLat); } /** * 将经纬度 转换为 地址 * * @param longitude 经度 * @param latitude 纬度 * @return 地址名称 * @throws Exception */ public static R getAddress(String longitude, String latitude) throws Exception { String url; try { url = "http://restapi.amap.com/v3/geocode/regeo?output=JSON&location=" + longitude + "," + latitude + "&key=" + GAO_DE_KEY + "&radius=0&extensions=base"; log.info("经度 " + longitude); log.info("纬度:" + latitude); log.info("url:" + url); // 高德接口返回的是JSON格式的字符串 String queryResult = getResponse(url); if (ObjectUtils.isNull(queryResult)) { return R.fail("查询结果为空"); } // 将获取结果转为json 数据 JSONObject obj = JSONObject.parseObject(queryResult); if (obj.get(GaoDeEnum.STATUS.getCode()).toString().equals(GaoDeEnum.INT_ONE.getCode())) { // 如果没有返回-1 JSONObject reGeoCode = obj.getJSONObject(GaoDeEnum.RE_GEO_CODE.getCode()); if (reGeoCode.size() > 0) { log.info("reGeoCode:" + reGeoCode); // 在regeocode中拿到 formatted_address 具体位置 String formatted = reGeoCode.get("formatted_address").toString(); return R.ok( "地址获取成功!",formatted); } else { return R.fail("未找到相匹配的地址!"); } } else { return R.fail("请求错误!"); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return R.fail("系统未知异常,请稍后再试"); } } /** * 功能描述: 根据两个定位点的经纬度算出两点间的距离 * * @param startLonLat 起始经纬度 * @param endLonLat 结束经纬度(目标经纬度) * @return java.lang.Long 两个定位点之间的距离 * @author isymikasan * @date 2022-01-26 09:47:42 */ public R getDistance(String startLonLat, String endLonLat) { try { // 返回起始地startAddr与目的地endAddr之间的距离,单位:米 Long result = new Long(0); String queryUrl = "http://restapi.amap.com/v3/distance?key=" + GAO_DE_KEY + "&origins=" + startLonLat + "&destination=" + endLonLat; String queryResult = getResponse(queryUrl); log.info("请求url is {} \n高德接口返回的是JSON格式的字符串:{}" ,queryUrl,queryResult); JSONObject job = JSONObject.parseObject(queryResult); JSONArray ja = job.getJSONArray("results"); if(ja.size() == 0){ return R.ok(0L); //距离计算失败 } JSONObject jobO = JSONObject.parseObject(ja.getString(0)); result = Long.parseLong(jobO.get("distance").toString()); return R.ok("距离计算成功!",result); } catch (Exception e) { return R.fail(e.toString()); } } /** * 功能描述: 发送请求 * * @param serverUrl 请求地址 * @return java.lang.String * @author isymikasan * @date 2022-01-26 09:15:01 */ private static String getResponse(String serverUrl) { // 用JAVA发起http请求,并返回json格式的结果 StringBuffer result = new StringBuffer(); try { URL url = new URL(serverUrl); URLConnection conn = url.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result.append(line); } in.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result.toString(); } public static void main(String[] args) throws Exception { System.out.println(getAddress( "118.73145","32.00335")); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/map/City.java ================================================ package top.flya.system.utils.map; public class City { private String name; public String getName() { return name; } public void setName(String name) { if(name.endsWith("市")) { name = name.substring(0, name.length() - 1); } this.name = name; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/map/CitySql.java ================================================ package top.flya.system.utils.map; import com.alibaba.fastjson.JSONObject; import lombok.Data; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.util.List; @Data public class CitySql { public static void main(String[] args) throws IOException { try { File file = new File("J:\\ReStudyVue\\paizhi-city\\PaiZhiCheng\\src\\main\\java\\top\\flya\\system\\utils\\map\\city.json"); String content = FileUtils.readFileToString(file, "UTF-8"); // Do something with the data System.out.println("Hello World!"); List maps = JSONObject.parseArray(content, Maps.class); // List maps = JsonUtils.parseArray(content, Maps.class); String sql = "INSERT INTO paizhicheng.pzc_region (base, name, img_url, create_time, update_time, state)\n" + "VALUES ('江苏省', '扬州', DEFAULT, DEFAULT, DEFAULT, DEFAULT);"; StringBuilder sb = new StringBuilder(); for (Maps map : maps) { String base = map.getName(); List city = map.getCity(); for (City maps1 : city) { String name = maps1.getName(); String sql1 = sql.replace("江苏省", base).replace("扬州", name); System.out.println(sql1); sb.append(sql1).append("\n"); } } System.out.println(sb.toString()); } catch (IOException e) { e.printStackTrace(); } } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/map/Maps.java ================================================ package top.flya.system.utils.map; import lombok.Data; import java.util.List; @Data public class Maps { private String name; private List city; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/map/city.json ================================================ [ { "name": "北京市", "city": [ { "name": "北京市", "area": [ "东城区", "西城区", "崇文区", "宣武区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区", "平谷区", "怀柔区", "密云县", "延庆县" ] } ] }, { "name": "天津市", "city": [ { "name": "天津市", "area": [ "和平区", "河东区", "河西区", "南开区", "河北区", "红桥区", "塘沽区", "汉沽区", "大港区", "东丽区", "西青区", "津南区", "北辰区", "武清区", "宝坻区", "宁河县", "静海县", "蓟 县" ] } ] }, { "name": "河北省", "city": [ { "name": "石家庄市", "area": [ "长安区", "桥东区", "桥西区", "新华区", "郊 区", "井陉矿区", "井陉县", "正定县", "栾城县", "行唐县", "灵寿县", "高邑县", "深泽县", "赞皇县", "无极县", "平山县", "元氏县", "赵 县", "辛集市", "藁", "晋州市", "新乐市", "鹿泉市" ] }, { "name": "唐山市", "area": [ "路南区", "路北区", "古冶区", "开平区", "新 区", "丰润县", "滦 县", "滦南县", "乐亭县", "迁西县", "玉田县", "唐海县", "遵化市", "丰南市", "迁安市" ] }, { "name": "秦皇岛市", "area": [ "海港区", "山海关区", "北戴河区", "青龙满族自治县", "昌黎县", "抚宁县", "卢龙县" ] }, { "name": "邯郸市", "area": [ "邯山区", "丛台区", "复兴区", "峰峰矿区", "邯郸县", "临漳县", "成安县", "大名县", "涉 县", "磁 县", "肥乡县", "永年县", "邱 县", "鸡泽县", "广平县", "馆陶县", "魏 县", "曲周县", "武安市" ] }, { "name": "邢台市", "area": [ "桥东区", "桥西区", "邢台县", "临城县", "内丘县", "柏乡县", "隆尧县", "任 县", "南和县", "宁晋县", "巨鹿县", "新河县", "广宗县", "平乡县", "威 县", "清河县", "临西县", "南宫市", "沙河市" ] }, { "name": "保定市", "area": [ "新市区", "北市区", "南市区", "满城县", "清苑县", "涞水县", "阜平县", "徐水县", "定兴县", "唐 县", "高阳县", "容城县", "涞源县", "望都县", "安新县", "易 县", "曲阳县", "蠡 县", "顺平县", "博野", "雄县", "涿州市", "定州市", "安国市", "高碑店市" ] }, { "name": "张家口", "area": [ "桥东区", "桥西区", "宣化区", "下花园区", "宣化县", "张北县", "康保县", "沽源县", "尚义县", "蔚 县", "阳原县", "怀安县", "万全县", "怀来县", "涿鹿县", "赤城县", "崇礼县" ] }, { "name": "承德市", "area": [ "双桥区", "双滦区", "鹰手营子矿区", "承德县", "兴隆县", "平泉县", "滦平县", "隆化县", "丰宁满族自治县", "宽城满族自治县", "围场满族蒙古族自治县" ] }, { "name": "沧州市", "area": [ "新华区", "运河区", "沧 县", "青 县", "东光县", "海兴县", "盐山县", "肃宁县", "南皮县", "吴桥县", "献 县", "孟村回族自治县", "泊头市", "任丘市", "黄骅市", "河间市" ] }, { "name": "廊坊市", "area": [ "安次区", "固安县", "永清县", "香河县", "大城县", "文安县", "大厂回族自治县", "霸州市", "三河市" ] }, { "name": "衡水市", "area": [ "桃城区", "枣强县", "武邑县", "武强县", "饶阳县", "安平县", "故城县", "景 县", "阜城县", "冀州市", "深州市" ] } ] }, { "name": "山西省", "city": [ { "name": "太原市", "area": [ "小店区", "迎泽区", "杏花岭区", "尖草坪区", "万柏林区", "晋源区", "清徐县", "阳曲县", "娄烦县", "古交市" ] }, { "name": "大同市", "area": [ "城 区", "矿 区", "南郊区", "新荣区", "阳高县", "天镇县", "广灵县", "灵丘县", "浑源县", "左云县", "大同县" ] }, { "name": "阳泉市", "area": [ "城 区", "矿 区", "郊 区", "平定县", "盂 县" ] }, { "name": "长治市", "area": [ "城 区", "郊 区", "长治县", "襄垣县", "屯留县", "平顺县", "黎城县", "壶关县", "长子县", "武乡县", "沁 县", "沁源县", "潞城市" ] }, { "name": "晋城市", "area": [ "城 区", "沁水县", "阳城县", "陵川县", "泽州县", "高平市" ] }, { "name": "朔州市", "area": [ "朔城区", "平鲁区", "山阴县", "应 县", "右玉县", "怀仁县" ] }, { "name": "忻州市", "area": [ "忻府区", "原平市", "定襄县", "五台县", "代 县", "繁峙县", "宁武县", "静乐县", "神池县", "五寨县", "岢岚县", "河曲县", "保德县", "偏关县" ] }, { "name": "吕梁市", "area": [ "离石区", "孝义市", "汾阳市", "文水县", "交城县", "兴 县", "临 县", "柳林县", "石楼县", "岚 县", "方山县", "中阳县", "交口县" ] }, { "name": "晋中市", "area": [ "榆次市", "介休市", "榆社县", "左权县", "和顺县", "昔阳县", "寿阳县", "太谷县", "祁 县", "平遥县", "灵石县" ] }, { "name": "临汾市", "area": [ "临汾市", "侯马市", "霍州市", "曲沃县", "翼城县", "襄汾县", "洪洞县", "古 县", "安泽县", "浮山县", "吉 县", "乡宁县", "蒲 县", "大宁县", "永和县", "隰 县", "汾西县" ] }, { "name": "运城市", "area": [ "运城市", "永济市", "河津市", "芮城县", "临猗县", "万荣县", "新绛县", "稷山县", "闻喜县", "夏 县", "绛 县", "平陆县", "垣曲县" ] } ] }, { "name": "内蒙古", "city": [ { "name": "呼和浩特市", "area": [ "新城区", "回民区", "玉泉区", "郊 区", "土默特左旗", "托克托县", "和林格尔县", "清水河县", "武川县" ] }, { "name": "包头市", "area": [ "东河区", "昆都伦区", "青山区", "石拐矿区", "白云矿区", "郊 区", "土默特右旗", "固阳县", "达尔罕茂明安联合旗" ] }, { "name": "乌海市", "area": [ "海勃湾区", "海南区", "乌达区" ] }, { "name": "赤峰市", "area": [ "红山区", "元宝山区", "松山区", "阿鲁科尔沁旗", "巴林左旗", "巴林右旗", "林西县", "克什克腾旗", "翁牛特旗", "喀喇沁旗", "宁城县", "敖汉旗" ] }, { "name": "呼伦贝尔市", "area": [ "海拉尔市", "满洲里市", "扎兰屯市", "牙克石市", "根河市", "额尔古纳市", "阿荣旗", "莫力达瓦达斡尔族自治旗", "鄂伦春自治旗", "鄂温克族自治旗", "新巴尔虎右旗", "新巴尔虎左旗", "陈巴尔虎旗" ] }, { "name": "兴安盟", "area": [ "乌兰浩特市", "阿尔山市", "科尔沁右翼前旗", "科尔沁右翼中旗", "扎赉特旗", "突泉县" ] }, { "name": "通辽市", "area": [ "科尔沁区", "霍林郭勒市", "科尔沁左翼中旗", "科尔沁左翼后旗", "开鲁县", "库伦旗", "奈曼旗", "扎鲁特旗" ] }, { "name": "锡林郭勒盟", "area": [ "二连浩特市", "锡林浩特市", "阿巴嘎旗", "苏尼特左旗", "苏尼特右旗", "东乌珠穆沁旗", "西乌珠穆沁旗", "太仆寺旗", "镶黄旗", "正镶白旗", "正蓝旗", "多伦县" ] }, { "name": "乌兰察布盟", "area": [ "集宁市", "丰镇市", "卓资县", "化德县", "商都县", "兴和县", "凉城县", "察哈尔右翼前旗", "察哈尔右翼中旗", "察哈尔右翼后旗", "四子王旗" ] }, { "name": "伊克昭盟", "area": [ "东胜市", "达拉特旗", "准格尔旗", "鄂托克前旗", "鄂托克旗", "杭锦旗", "乌审旗", "伊金霍洛旗" ] }, { "name": "巴彦淖尔盟", "area": [ "临河市", "五原县", "磴口县", "乌拉特前旗", "乌拉特中旗", "乌拉特后旗", "杭锦后旗" ] }, { "name": "阿拉善盟", "area": [ "阿拉善左旗", "阿拉善右旗", "额济纳旗" ] } ] }, { "name": "辽宁省", "city": [ { "name": "沈阳市", "area": [ "沈河区", "皇姑区", "和平区", "大东区", "铁西区", "苏家屯区", "东陵区", "于洪区", "新民市", "法库县", "辽中县", "康平县", "新城子区", "其他" ] }, { "name": "大连市", "area": [ "西岗区", "中山区", "沙河口区", "甘井子区", "旅顺口区", "金州区", "瓦房店市", "普兰店市", "庄河市", "长海县", "其他" ] }, { "name": "鞍山市", "area": [ "铁东区", "铁西区", "立山区", "千山区", "海城市", "台安县", "岫岩满族自治县", "其他" ] }, { "name": "抚顺市", "area": [ "顺城区", "新抚区", "东洲区", "望花区", "抚顺县", "清原满族自治县", "新宾满族自治县", "其他" ] }, { "name": "本溪市", "area": [ "平山区", "明山区", "溪湖区", "南芬区", "本溪满族自治县", "桓仁满族自治县", "其他" ] }, { "name": "丹东市", "area": [ "振兴区", "元宝区", "振安区", "东港市", "凤城市", "宽甸满族自治县", "其他" ] }, { "name": "锦州市", "area": [ "太和区", "古塔区", "凌河区", "凌海市", "黑山县", "义县", "北宁市", "其他" ] }, { "name": "营口市", "area": [ "站前区", "西市区", "鲅鱼圈区", "老边区", "大石桥市", "盖州市", "其他" ] }, { "name": "阜新市", "area": [ "海州区", "新邱区", "太平区", "清河门区", "细河区", "彰武县", "阜新蒙古族自治县", "其他" ] }, { "name": "辽阳市", "area": [ "白塔区", "文圣区", "宏伟区", "太子河区", "弓长岭区", "灯塔市", "辽阳县", "其他" ] }, { "name": "盘锦", "area": [ "双台子区", "兴隆台区", "盘山县", "大洼县", "其他" ] }, { "name": "铁岭市", "area": [ "银州区", "清河区", "调兵山市", "开原市", "铁岭县", "昌图县", "西丰县", "其他" ] }, { "name": "朝阳市", "area": [ "双塔区", "龙城区", "凌源市", "北票市", "朝阳县", "建平县", "喀喇沁左翼蒙古族自治县", "其他" ] }, { "name": "葫芦岛市", "area": [ "龙港区", "南票区", "连山区", "兴城市", "绥中县", "建昌县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "吉林省", "city": [ { "name": "长春市", "area": [ "朝阳区", "宽城区", "二道区", "南关区", "绿园区", "双阳区", "九台市", "榆树市", "德惠市", "农安县", "其他" ] }, { "name": "吉林市", "area": [ "船营区", "昌邑区", "龙潭区", "丰满区", "舒兰市", "桦甸市", "蛟河市", "磐石市", "永吉县", "其他" ] }, { "name": "四平", "area": [ "铁西区", "铁东区", "公主岭市", "双辽市", "梨树县", "伊通满族自治县", "其他" ] }, { "name": "辽源市", "area": [ "龙山区", "西安区", "东辽县", "东丰县", "其他" ] }, { "name": "通化市", "area": [ "东昌区", "二道江区", "梅河口市", "集安市", "通化县", "辉南县", "柳河县", "其他" ] }, { "name": "白山市", "area": [ "八道江区", "江源区", "临江市", "靖宇县", "抚松县", "长白朝鲜族自治县", "其他" ] }, { "name": "松原市", "area": [ "宁江区", "乾安县", "长岭县", "扶余县", "前郭尔罗斯蒙古族自治县", "其他" ] }, { "name": "白城市", "area": [ "洮北区", "大安市", "洮南市", "镇赉县", "通榆县", "其他" ] }, { "name": "延边朝鲜族自治州", "area": [ "延吉市", "图们市", "敦化市", "龙井市", "珲春市", "和龙市", "安图县", "汪清县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "黑龙江省", "city": [ { "name": "哈尔滨市", "area": [ "松北区", "道里区", "南岗区", "平房区", "香坊区", "道外区", "呼兰区", "阿城区", "双城市", "尚志市", "五常市", "宾县", "方正县", "通河县", "巴彦县", "延寿县", "木兰县", "依兰县", "其他" ] }, { "name": "齐齐哈尔市", "area": [ "龙沙区", "昂昂溪区", "铁锋区", "建华区", "富拉尔基区", "碾子山区", "梅里斯达斡尔族区", "讷河市", "富裕县", "拜泉县", "甘南县", "依安县", "克山县", "泰来县", "克东县", "龙江县", "其他" ] }, { "name": "鹤岗市", "area": [ "兴山区", "工农区", "南山区", "兴安区", "向阳区", "东山区", "萝北县", "绥滨县", "其他" ] }, { "name": "双鸭山", "area": [ "尖山区", "岭东区", "四方台区", "宝山区", "集贤县", "宝清县", "友谊县", "饶河县", "其他" ] }, { "name": "鸡西市", "area": [ "鸡冠区", "恒山区", "城子河区", "滴道区", "梨树区", "麻山区", "密山市", "虎林市", "鸡东县", "其他" ] }, { "name": "大庆市", "area": [ "萨尔图区", "红岗区", "龙凤区", "让胡路区", "大同区", "林甸县", "肇州县", "肇源县", "杜尔伯特蒙古族自治县", "其他" ] }, { "name": "伊春市", "area": [ "伊春区", "带岭区", "南岔区", "金山屯区", "西林区", "美溪区", "乌马河区", "翠峦区", "友好区", "上甘岭区", "五营区", "红星区", "新青区", "汤旺河区", "乌伊岭区", "铁力市", "嘉荫县", "其他" ] }, { "name": "牡丹江市", "area": [ "爱民区", "东安区", "阳明区", "西安区", "绥芬河市", "宁安市", "海林市", "穆棱市", "林口县", "东宁县", "其他" ] }, { "name": "佳木斯市", "area": [ "向阳区", "前进区", "东风区", "郊区", "同江市", "富锦市", "桦川县", "抚远县", "桦南县", "汤原县", "其他" ] }, { "name": "七台河市", "area": [ "桃山区", "新兴区", "茄子河区", "勃利县", "其他" ] }, { "name": "黑河市", "area": [ "爱辉区", "北安市", "五大连池市", "逊克县", "嫩江县", "孙吴县", "其他" ] }, { "name": "绥化市", "area": [ "北林区", "安达市", "肇东市", "海伦市", "绥棱县", "兰西县", "明水县", "青冈县", "庆安县", "望奎县", "其他" ] }, { "name": "大兴安岭地区", "area": [ "呼玛县", "塔河县", "漠河县", "大兴安岭辖区", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "上海市", "city": [ { "name": "上海市", "area": [ "黄浦区", "卢湾区", "徐汇区", "长宁区", "静安区", "普陀区", "闸北区", "虹口区", "杨浦区", "宝山区", "闵行区", "嘉定区", "松江区", "金山区", "青浦区", "南汇区", "奉贤区", "浦东新区", "崇明县", "其他" ] } ] }, { "name": "江苏省", "city": [ { "name": "南京市", "area": [ "玄武区", "白下区", "秦淮区", "建邺区", "鼓楼区", "下关区", "栖霞区", "雨花台区", "浦口区", "江宁区", "六合区", "溧水县", "高淳县", "其他" ] }, { "name": "苏州市", "area": [ "金阊区", "平江区", "沧浪区", "虎丘区", "吴中区", "相城区", "常熟市", "张家港市", "昆山市", "吴江市", "太仓市", "其他" ] }, { "name": "无锡市", "area": [ "崇安区", "南长区", "北塘区", "滨湖区", "锡山区", "惠山区", "江阴市", "宜兴市", "其他" ] }, { "name": "常州市", "area": [ "钟楼区", "天宁区", "戚墅堰区", "新北区", "武进区", "金坛市", "溧阳市", "其他" ] }, { "name": "镇江市", "area": [ "京口区", "润州区", "丹徒区", "丹阳市", "扬中市", "句容市", "其他" ] }, { "name": "南通市", "area": [ "崇川区", "港闸区", "通州市", "如皋市", "海门市", "启东市", "海安县", "如东县", "其他" ] }, { "name": "泰州市", "area": [ "海陵区", "高港区", "姜堰市", "泰兴市", "靖江市", "兴化市", "其他" ] }, { "name": "扬州市", "area": [ "广陵区", "维扬区", "邗江区", "江都市", "仪征市", "高邮市", "宝应县", "其他" ] }, { "name": "盐城市", "area": [ "亭湖区", "盐都区", "大丰市", "东台市", "建湖县", "射阳县", "阜宁县", "滨海县", "响水县", "其他" ] }, { "name": "连云港市", "area": [ "新浦区", "海州区", "连云区", "东海县", "灌云县", "赣榆县", "灌南县", "其他" ] }, { "name": "徐州市", "area": [ "云龙区", "鼓楼区", "九里区", "泉山区", "贾汪区", "邳州市", "新沂市", "铜山县", "睢宁县", "沛县", "丰县", "其他" ] }, { "name": "淮安市", "area": [ "清河区", "清浦区", "楚州区", "淮阴区", "涟水县", "洪泽县", "金湖县", "盱眙县", "其他" ] }, { "name": "宿迁市", "area": [ "宿城区", "宿豫区", "沭阳县", "泗阳县", "泗洪县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "浙江省", "city": [ { "name": "杭州市", "area": [ "拱墅区", "西湖区", "上城区", "下城区", "江干区", "滨江区", "余杭区", "萧山区", "建德市", "富阳市", "临安市", "桐庐县", "淳安县", "其他" ] }, { "name": "宁波市", "area": [ "海曙区", "江东区", "江北区", "镇海区", "北仑区", "鄞州区", "余姚市", "慈溪市", "奉化市", "宁海县", "象山县", "其他" ] }, { "name": "温州市", "area": [ "鹿城区", "龙湾区", "瓯海区", "瑞安市", "乐清市", "永嘉县", "洞头县", "平阳县", "苍南县", "文成县", "泰顺县", "其他" ] }, { "name": "嘉兴市", "area": [ "秀城区", "秀洲区", "海宁市", "平湖市", "桐乡市", "嘉善县", "海盐县", "其他" ] }, { "name": "湖州市", "area": [ "吴兴区", "南浔区", "长兴县", "德清县", "安吉县", "其他" ] }, { "name": "绍兴市", "area": [ "越城区", "诸暨市", "上虞市", "嵊州市", "绍兴县", "新昌县", "其他" ] }, { "name": "金华市", "area": [ "婺城区", "金东区", "兰溪市", "义乌市", "东阳市", "永康市", "武义县", "浦江县", "磐安县", "其他" ] }, { "name": "衢州市", "area": [ "柯城区", "衢江区", "江山市", "龙游县", "常山县", "开化县", "其他" ] }, { "name": "舟山市", "area": [ "定海区", "普陀区", "岱山县", "嵊泗县", "其他" ] }, { "name": "台州市", "area": [ "椒江区", "黄岩区", "路桥区", "临海市", "温岭市", "玉环县", "天台县", "仙居县", "三门县", "其他" ] }, { "name": "丽水市", "area": [ "莲都区", "龙泉市", "缙云县", "青田县", "云和县", "遂昌县", "松阳县", "庆元县", "景宁畲族自治县", "其他" ] }, { "name": "其他市", "area": [ "其他" ] } ] }, { "name": "安徽省", "city": [ { "name": "合肥市", "area": [ "庐阳区", "瑶海区", "蜀山区", "包河区", "长丰县", "肥东县", "肥西县", "其他" ] }, { "name": "芜湖市", "area": [ "镜湖区", "弋江区", "鸠江区", "三山区", "芜湖县", "南陵县", "繁昌县", "其他" ] }, { "name": "蚌埠市", "area": [ "蚌山区", "龙子湖区", "禹会区", "淮上区", "怀远县", "固镇县", "五河县", "其他" ] }, { "name": "淮南市", "area": [ "田家庵区", "大通区", "谢家集区", "八公山区", "潘集区", "凤台县", "其他" ] }, { "name": "马鞍山市", "area": [ "雨山区", "花山区", "金家庄区", "当涂县", "其他" ] }, { "name": "淮北市", "area": [ "相山区", "杜集区", "烈山区", "濉溪县", "其他" ] }, { "name": "铜陵市", "area": [ "铜官山区", "狮子山区", "郊区", "铜陵县", "其他" ] }, { "name": "安庆市", "area": [ "迎江区", "大观区", "宜秀区", "桐城市", "宿松县", "枞阳县", "太湖县", "怀宁县", "岳西县", "望江县", "潜山县", "其他" ] }, { "name": "黄山市", "area": [ "屯溪区", "黄山区", "徽州区", "休宁县", "歙县", "祁门县", "黟县", "其他" ] }, { "name": "滁州市", "area": [ "琅琊区", "南谯区", "天长市", "明光市", "全椒县", "来安县", "定远县", "凤阳县", "其他" ] }, { "name": "阜阳市", "area": [ "颍州区", "颍东区", "颍泉区", "界首市", "临泉县", "颍上县", "阜南县", "太和县", "其他" ] }, { "name": "宿州市", "area": [ "埇桥区", "萧县", "泗县", "砀山县", "灵璧县", "其他" ] }, { "name": "巢湖市", "area": [ "居巢区", "含山县", "无为县", "庐江县", "和县", "其他" ] }, { "name": "六安市", "area": [ "金安区", "裕安区", "寿县", "霍山县", "霍邱县", "舒城县", "金寨县", "其他" ] }, { "name": "亳州市", "area": [ "谯城区", "利辛县", "涡阳县", "蒙城县", "其他" ] }, { "name": "池州市", "area": [ "贵池区", "东至县", "石台县", "青阳县", "其他" ] }, { "name": "宣城市", "area": [ "宣州区", "宁国市", "广德县", "郎溪县", "泾县", "旌德县", "绩溪县", "其他" ] }, { "name": "其他市", "area": [ "其他" ] } ] }, { "name": "福建省", "city": [ { "name": "福州市", "area": [ "鼓楼区", "台江区", "仓山区", "马尾区", "晋安区", "福清市", "长乐市", "闽侯县", "闽清县", "永泰县", "连江县", "罗源县", "平潭县", "其他" ] }, { "name": "厦门市", "area": [ "思明区", "海沧区", "湖里区", "集美区", "同安区", "翔安区", "其他" ] }, { "name": "莆田市", "area": [ "城厢区", "涵江区", "荔城区", "秀屿区", "仙游县", "其他" ] }, { "name": "三明市", "area": [ "梅列区", "三元区", "永安市", "明溪县", "将乐县", "大田县", "宁化县", "建宁县", "沙县", "尤溪县", "清流县", "泰宁县", "其他" ] }, { "name": "泉州市", "area": [ "鲤城区", "丰泽区", "洛江区", "泉港区", "石狮市", "晋江市", "南安市", "惠安县", "永春县", "安溪县", "德化县", "金门县", "其他" ] }, { "name": "漳州市", "area": [ "芗城区", "龙文区", "龙海市", "平和县", "南靖县", "诏安县", "漳浦县", "华安县", "东山县", "长泰县", "云霄县", "其他" ] }, { "name": "南平市", "area": [ "延平区", "建瓯市", "邵武市", "武夷山市", "建阳市", "松溪县", "光泽县", "顺昌县", "浦城县", "政和县", "其他" ] }, { "name": "龙岩市", "area": [ "新罗区", "漳平市", "长汀县", "武平县", "上杭县", "永定县", "连城县", "其他" ] }, { "name": "宁德市", "area": [ "蕉城区", "福安市", "福鼎市", "寿宁县", "霞浦县", "柘荣县", "屏南县", "古田县", "周宁县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "江西省", "city": [ { "name": "南昌市", "area": [ "东湖区", "西湖区", "青云谱区", "湾里区", "青山湖区", "新建县", "南昌县", "进贤县", "安义县", "其他" ] }, { "name": "景德镇市", "area": [ "珠山区", "昌江区", "乐平市", "浮梁县", "其他" ] }, { "name": "萍乡市", "area": [ "安源区", "湘东区", "莲花县", "上栗县", "芦溪县", "其他" ] }, { "name": "九江市", "area": [ "浔阳区", "庐山区", "瑞昌市", "九江县", "星子县", "武宁县", "彭泽县", "永修县", "修水县", "湖口县", "德安县", "都昌县", "其他" ] }, { "name": "新余市", "area": [ "渝水区", "分宜县", "其他" ] }, { "name": "鹰潭市", "area": [ "月湖区", "贵溪市", "余江县", "其他" ] }, { "name": "赣州市", "area": [ "章贡区", "瑞金市", "南康市", "石城县", "安远县", "赣县", "宁都县", "寻乌县", "兴国县", "定南县", "上犹县", "于都县", "龙南县", "崇义县", "信丰县", "全南县", "大余县", "会昌县", "其他" ] }, { "name": "吉安市", "area": [ "吉州区", "青原区", "井冈山市", "吉安县", "永丰县", "永新县", "新干县", "泰和县", "峡江县", "遂川县", "安福县", "吉水县", "万安县", "其他" ] }, { "name": "宜春市", "area": [ "袁州区", "丰城市", "樟树市", "高安市", "铜鼓县", "靖安县", "宜丰县", "奉新县", "万载县", "上高县", "其他" ] }, { "name": "抚州市", "area": [ "临川区", "南丰县", "乐安县", "金溪县", "南城县", "东乡县", "资溪县", "宜黄县", "广昌县", "黎川县", "崇仁县", "其他" ] }, { "name": "上饶市", "area": [ "信州区", "德兴市", "上饶县", "广丰县", "鄱阳县", "婺源县", "铅山县", "余干县", "横峰县", "弋阳县", "玉山县", "万年县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "山东省", "city": [ { "name": "济南市", "area": [ "市中区", "历下区", "天桥区", "槐荫区", "历城区", "长清区", "章丘市", "平阴县", "济阳县", "商河县", "其他" ] }, { "name": "青岛市", "area": [ "市南区", "市北区", "城阳区", "四方区", "李沧区", "黄岛区", "崂山区", "胶南市", "胶州市", "平度市", "莱西市", "即墨市", "其他" ] }, { "name": "淄博市", "area": [ "张店区", "临淄区", "淄川区", "博山区", "周村区", "桓台县", "高青县", "沂源县", "其他" ] }, { "name": "枣庄市", "area": [ "市中区", "山亭区", "峄城区", "台儿庄区", "薛城区", "滕州市", "其他" ] }, { "name": "东营市", "area": [ "东营区", "河口区", "垦利县", "广饶县", "利津县", "其他" ] }, { "name": "烟台市", "area": [ "芝罘区", "福山区", "牟平区", "莱山区", "龙口市", "莱阳市", "莱州市", "招远市", "蓬莱市", "栖霞市", "海阳市", "长岛县", "其他" ] }, { "name": "潍坊市", "area": [ "潍城区", "寒亭区", "坊子区", "奎文区", "青州市", "诸城市", "寿光市", "安丘市", "高密市", "昌邑市", "昌乐县", "临朐县", "其他" ] }, { "name": "济宁市", "area": [ "市中区", "任城区", "曲阜市", "兖州市", "邹城市", "鱼台县", "金乡县", "嘉祥县", "微山县", "汶上县", "泗水县", "梁山县", "其他" ] }, { "name": "泰安市", "area": [ "泰山区", "岱岳区", "新泰市", "肥城市", "宁阳县", "东平县", "其他" ] }, { "name": "威海市", "area": [ "环翠区", "乳山市", "文登市", "荣成市", "其他" ] }, { "name": "日照市", "area": [ "东港区", "岚山区", "五莲县", "莒县", "其他" ] }, { "name": "莱芜市", "area": [ "莱城区", "钢城区", "其他" ] }, { "name": "临沂市", "area": [ "兰山区", "罗庄区", "河东区", "沂南县", "郯城县", "沂水县", "苍山县", "费县", "平邑县", "莒南县", "蒙阴县", "临沭县", "其他" ] }, { "name": "德州市", "area": [ "德城区", "乐陵市", "禹城市", "陵县", "宁津县", "齐河县", "武城县", "庆云县", "平原县", "夏津县", "临邑县", "其他" ] }, { "name": "聊城市", "area": [ "东昌府区", "临清市", "高唐县", "阳谷县", "茌平县", "莘县", "东阿县", "冠县", "其他" ] }, { "name": "滨州市", "area": [ "滨城区", "邹平县", "沾化县", "惠民县", "博兴县", "阳信县", "无棣县", "其他" ] }, { "name": "菏泽市", "area": [ "牡丹区", "鄄城县", "单县", "郓城县", "曹县", "定陶县", "巨野县", "东明县", "成武县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "河南省", "city": [ { "name": "郑州市", "area": [ "中原区", "金水区", "二七区", "管城回族区", "上街区", "惠济区", "巩义市", "新郑市", "新密市", "登封市", "荥阳市", "中牟县", "其他" ] }, { "name": "开封市", "area": [ "鼓楼区", "龙亭区", "顺河回族区", "禹王台区", "金明区", "开封县", "尉氏县", "兰考县", "杞县", "通许县", "其他" ] }, { "name": "洛阳市", "area": [ "西工区", "老城区", "涧西区", "瀍河回族区", "洛龙区", "吉利区", "偃师市", "孟津县", "汝阳县", "伊川县", "洛宁县", "嵩县", "宜阳县", "新安县", "栾川县", "其他" ] }, { "name": "平顶山市", "area": [ "新华区", "卫东区", "湛河区", "石龙区", "汝州市", "舞钢市", "宝丰县", "叶县", "郏县", "鲁山县", "其他" ] }, { "name": "安阳市", "area": [ "北关区", "文峰区", "殷都区", "龙安区", "林州市", "安阳县", "滑县", "内黄县", "汤阴县", "其他" ] }, { "name": "鹤壁市", "area": [ "淇滨区", "山城区", "鹤山区", "浚县", "淇县", "其他" ] }, { "name": "新乡市", "area": [ "卫滨区", "红旗区", "凤泉区", "牧野区", "卫辉市", "辉县市", "新乡县", "获嘉县", "原阳县", "长垣县", "封丘县", "延津县", "其他" ] }, { "name": "焦作市", "area": [ "解放区", "中站区", "马村区", "山阳区", "沁阳市", "孟州市", "修武县", "温县", "武陟县", "博爱县", "其他" ] }, { "name": "濮阳市", "area": [ "华龙区", "濮阳县", "南乐县", "台前县", "清丰县", "范县", "其他" ] }, { "name": "许昌市", "area": [ "魏都区", "禹州市", "长葛市", "许昌县", "鄢陵县", "襄城县", "其他" ] }, { "name": "漯河市", "area": [ "源汇区", "郾城区", "召陵区", "临颍县", "舞阳县", "其他" ] }, { "name": "三门峡市", "area": [ "湖滨区", "义马市", "灵宝市", "渑池县", "卢氏县", "陕县", "其他" ] }, { "name": "南阳市", "area": [ "卧龙区", "宛城区", "邓州市", "桐柏县", "方城县", "淅川县", "镇平县", "唐河县", "南召县", "内乡县", "新野县", "社旗县", "西峡县", "其他" ] }, { "name": "商丘市", "area": [ "梁园区", "睢阳区", "永城市", "宁陵县", "虞城县", "民权县", "夏邑县", "柘城县", "睢县", "其他" ] }, { "name": "信阳市", "area": [ "浉河区", "平桥区", "潢川县", "淮滨县", "息县", "新县", "商城县", "固始县", "罗山县", "光山县", "其他" ] }, { "name": "周口市", "area": [ "川汇区", "项城市", "商水县", "淮阳县", "太康县", "鹿邑县", "西华县", "扶沟县", "沈丘县", "郸城县", "其他" ] }, { "name": "驻马店市", "area": [ "驿城区", "确山县", "新蔡县", "上蔡县", "西平县", "泌阳县", "平舆县", "汝南县", "遂平县", "正阳县", "其他" ] }, { "name": "焦作市", "area": [ "济源市", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "湖北省", "city": [ { "name": "武汉市", "area": [ "江岸区", "武昌区", "江汉区", "硚口区", "汉阳区", "青山区", "洪山区", "东西湖区", "汉南区", "蔡甸区", "江夏区", "黄陂区", "新洲区", "其他" ] }, { "name": "黄石市", "area": [ "黄石港区", "西塞山区", "下陆区", "铁山区", "大冶市", "阳新县", "其他" ] }, { "name": "十堰市", "area": [ "张湾区", "茅箭区", "丹江口市", "郧县", "竹山县", "房县", "郧西县", "竹溪县", "其他" ] }, { "name": "荆州市", "area": [ "沙市区", "荆州区", "洪湖市", "石首市", "松滋市", "监利县", "公安县", "江陵县", "其他" ] }, { "name": "宜昌市", "area": [ "西陵区", "伍家岗区", "点军区", "猇亭区", "夷陵区", "宜都市", "当阳市", "枝江市", "秭归县", "远安县", "兴山县", "五峰土家族自治县", "长阳土家族自治县", "其他" ] }, { "name": "襄樊市", "area": [ "襄城区", "樊城区", "襄阳区", "老河口市", "枣阳市", "宜城市", "南漳县", "谷城县", "保康县", "其他" ] }, { "name": "鄂州市", "area": [ "鄂城区", "华容区", "梁子湖区", "其他" ] }, { "name": "荆门市", "area": [ "东宝区", "掇刀区", "钟祥市", "京山县", "沙洋县", "其他" ] }, { "name": "孝感市", "area": [ "孝南区", "应城市", "安陆市", "汉川市", "云梦县", "大悟县", "孝昌县", "其他" ] }, { "name": "黄冈市", "area": [ "黄州区", "麻城市", "武穴市", "红安县", "罗田县", "浠水县", "蕲春县", "黄梅县", "英山县", "团风县", "其他" ] }, { "name": "咸宁市", "area": [ "咸安区", "赤壁市", "嘉鱼县", "通山县", "崇阳县", "通城县", "其他" ] }, { "name": "随州市", "area": [ "曾都区", "广水市", "其他" ] }, { "name": "恩施土家族苗族自治州", "area": [ "恩施市", "利川市", "建始县", "来凤县", "巴东县", "鹤峰县", "宣恩县", "咸丰县", "其他" ] }, { "name": "仙桃市", "area": [ "仙桃" ] }, { "name": "天门市", "area": [ "天门" ] }, { "name": "潜江市", "area": [ "潜江" ] }, { "name": "神农架林区", "area": [ "神农架林区" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "湖南省", "city": [ { "name": "长沙市", "area": [ "岳麓区", "芙蓉区", "天心区", "开福区", "雨花区", "浏阳市", "长沙县", "望城县", "宁乡县", "其他" ] }, { "name": "株洲市", "area": [ "天元区", "荷塘区", "芦淞区", "石峰区", "醴陵市", "株洲县", "炎陵县", "茶陵县", "攸县", "其他" ] }, { "name": "湘潭市", "area": [ "岳塘区", "雨湖区", "湘乡市", "韶山市", "湘潭县", "其他" ] }, { "name": "衡阳市", "area": [ "雁峰区", "珠晖区", "石鼓区", "蒸湘区", "南岳区", "耒阳市", "常宁市", "衡阳县", "衡东县", "衡山县", "衡南县", "祁东县", "其他" ] }, { "name": "邵阳市", "area": [ "双清区", "大祥区", "北塔区", "武冈市", "邵东县", "洞口县", "新邵县", "绥宁县", "新宁县", "邵阳县", "隆回县", "城步苗族自治县", "其他" ] }, { "name": "岳阳市", "area": [ "岳阳楼区", "云溪区", "君山区", "临湘市", "汨罗市", "岳阳县", "湘阴县", "平江县", "华容县", "其他" ] }, { "name": "常德市", "area": [ "武陵区", "鼎城区", "津市市", "澧县", "临澧县", "桃源县", "汉寿县", "安乡县", "石门县", "其他" ] }, { "name": "张家界市", "area": [ "永定区", "武陵源区", "慈利县", "桑植县", "其他" ] }, { "name": "益阳市", "area": [ "赫山区", "资阳区", "沅江市", "桃江县", "南县", "安化县", "其他" ] }, { "name": "郴州市", "area": [ "北湖区", "苏仙区", "资兴市", "宜章县", "汝城县", "安仁县", "嘉禾县", "临武县", "桂东县", "永兴县", "桂阳县", "其他" ] }, { "name": "永州市", "area": [ "冷水滩区", "零陵区", "祁阳县", "蓝山县", "宁远县", "新田县", "东安县", "江永县", "道县", "双牌县", "江华瑶族自治县", "其他" ] }, { "name": "怀化市", "area": [ "鹤城区", "洪江市", "会同县", "沅陵县", "辰溪县", "溆浦县", "中方县", "新晃侗族自治县", "芷江侗族自治县", "通道侗族自治县", "靖州苗族侗族自治县", "麻阳苗族自治县", "其他" ] }, { "name": "娄底市", "area": [ "娄星区", "冷水江市", "涟源市", "新化县", "双峰县", "其他" ] }, { "name": "湘西土家族苗族自治州", "area": [ "吉首市", "古丈县", "龙山县", "永顺县", "凤凰县", "泸溪县", "保靖县", "花垣县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "广东省", "city": [ { "name": "广州市", "area": [ "越秀区", "荔湾区", "海珠区", "天河区", "白云区", "黄埔区", "番禺区", "花都区", "南沙区", "萝岗区", "增城市", "从化市", "其他" ] }, { "name": "深圳市", "area": [ "福田区", "罗湖区", "南山区", "宝安区", "龙岗区", "盐田区", "其他" ] }, { "name": "东莞市", "area": [ "莞城", "常平", "塘厦", "塘厦", "塘厦", "其他" ] }, { "name": "中山市", "area": [ "中山" ] }, { "name": "潮州市", "area": [ "湘桥区", "潮安县", "饶平县", "其他" ] }, { "name": "揭阳市", "area": [ "榕城区", "揭东县", "揭西县", "惠来县", "普宁市", "其他" ] }, { "name": "云浮市", "area": [ "云城区", "新兴县", "郁南县", "云安县", "罗定市", "其他" ] }, { "name": "珠海市", "area": [ "香洲区", "斗门区", "金湾区", "其他" ] }, { "name": "汕头市", "area": [ "金平区", "濠江区", "龙湖区", "潮阳区", "潮南区", "澄海区", "南澳县", "其他" ] }, { "name": "韶关市", "area": [ "浈江区", "武江区", "曲江区", "乐昌市", "南雄市", "始兴县", "仁化县", "翁源县", "新丰县", "乳源瑶族自治县", "其他" ] }, { "name": "佛山市", "area": [ "禅城区", "南海区", "顺德区", "三水区", "高明区", "其他" ] }, { "name": "江门市", "area": [ "蓬江区", "江海区", "新会区", "恩平市", "台山市", "开平市", "鹤山市", "其他" ] }, { "name": "湛江市", "area": [ "赤坎区", "霞山区", "坡头区", "麻章区", "吴川市", "廉江市", "雷州市", "遂溪县", "徐闻县", "其他" ] }, { "name": "茂名市", "area": [ "茂南区", "茂港区", "化州市", "信宜市", "高州市", "电白县", "其他" ] }, { "name": "肇庆市", "area": [ "端州区", "鼎湖区", "高要市", "四会市", "广宁县", "怀集县", "封开县", "德庆县", "其他" ] }, { "name": "惠州市", "area": [ "惠城区", "惠阳区", "博罗县", "惠东县", "龙门县", "其他" ] }, { "name": "梅州市", "area": [ "梅江区", "兴宁市", "梅县", "大埔县", "丰顺县", "五华县", "平远县", "蕉岭县", "其他" ] }, { "name": "汕尾市", "area": [ "城区", "陆丰市", "海丰县", "陆河县", "其他" ] }, { "name": "河源市", "area": [ "源城区", "紫金县", "龙川县", "连平县", "和平县", "东源县", "其他" ] }, { "name": "阳江市", "area": [ "江城区", "阳春市", "阳西县", "阳东县", "其他" ] }, { "name": "清远市", "area": [ "清城区", "英德市", "连州市", "佛冈县", "阳山县", "清新县", "连山壮族瑶族自治县", "连南瑶族自治县", "其他" ] } ] }, { "name": "广西", "city": [ { "name": "南宁市", "area": [ "青秀区", "兴宁区", "西乡塘区", "良庆区", "江南区", "邕宁区", "武鸣县", "隆安县", "马山县", "上林县", "宾阳县", "横县", "其他" ] }, { "name": "柳州市", "area": [ "城中区", "鱼峰区", "柳北区", "柳南区", "柳江县", "柳城县", "鹿寨县", "融安县", "融水苗族自治县", "三江侗族自治县", "其他" ] }, { "name": "桂林市", "area": [ "象山区", "秀峰区", "叠彩区", "七星区", "雁山区", "阳朔县", "临桂县", "灵川县", "全州县", "平乐县", "兴安县", "灌阳县", "荔浦县", "资源县", "永福县", "龙胜各族自治县", "恭城瑶族自治县", "其他" ] }, { "name": "梧州市", "area": [ "万秀区", "蝶山区", "长洲区", "岑溪市", "苍梧县", "藤县", "蒙山县", "其他" ] }, { "name": "北海市", "area": [ "海城区", "银海区", "铁山港区", "合浦县", "其他" ] }, { "name": "防城港市", "area": [ "港口区", "防城区", "东兴市", "上思县", "其他" ] }, { "name": "钦州市", "area": [ "钦南区", "钦北区", "灵山县", "浦北县", "其他" ] }, { "name": "贵港市", "area": [ "港北区", "港南区", "覃塘区", "桂平市", "平南县", "其他" ] }, { "name": "玉林市", "area": [ "玉州区", "北流市", "容县", "陆川县", "博白县", "兴业县", "其他" ] }, { "name": "百色市", "area": [ "右江区", "凌云县", "平果县", "西林县", "乐业县", "德保县", "田林县", "田阳县", "靖西县", "田东县", "那坡县", "隆林各族自治县", "其他" ] }, { "name": "贺州市", "area": [ "八步区", "钟山县", "昭平县", "富川瑶族自治县", "其他" ] }, { "name": "河池市", "area": [ "金城江区", "宜州市", "天峨县", "凤山县", "南丹县", "东兰县", "都安瑶族自治县", "罗城仫佬族自治县", "巴马瑶族自治县", "环江毛南族自治县", "大化瑶族自治县", "其他" ] }, { "name": "来宾市", "area": [ "兴宾区", "合山市", "象州县", "武宣县", "忻城县", "金秀瑶族自治县", "其他" ] }, { "name": "崇左市", "area": [ "江州区", "凭祥市", "宁明县", "扶绥县", "龙州县", "大新县", "天等县", "其他" ] }, { "name": "其他市", "area": [ "其他" ] } ] }, { "name": "海南省", "city": [ { "name": "海口市", "area": [ "龙华区", "秀英区", "琼山区", "美兰区", "其他" ] }, { "name": "三亚市", "area": [ "三亚市", "其他" ] }, { "name": "五指山市", "area": [ "五指山" ] }, { "name": "琼海市", "area": [ "琼海" ] }, { "name": "儋州市", "area": [ "儋州" ] }, { "name": "文昌市", "area": [ "文昌" ] }, { "name": "万宁市", "area": [ "万宁" ] }, { "name": "东方市", "area": [ "东方" ] }, { "name": "澄迈县", "area": [ "澄迈县" ] }, { "name": "定安县", "area": [ "定安县" ] }, { "name": "屯昌县", "area": [ "屯昌县" ] }, { "name": "临高县", "area": [ "临高县" ] }, { "name": "白沙黎族自治县", "area": [ "白沙黎族自治县" ] }, { "name": "昌江黎族自治县", "area": [ "昌江黎族自治县" ] }, { "name": "乐东黎族自治县", "area": [ "乐东黎族自治县" ] }, { "name": "陵水黎族自治县", "area": [ "陵水黎族自治县" ] }, { "name": "保亭黎族苗族自治县", "area": [ "保亭黎族苗族自治县" ] }, { "name": "琼中黎族苗族自治县", "area": [ "琼中黎族苗族自治县" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "重庆市", "city": [ { "name": "重庆市", "area": [ "渝中区", "大渡口区", "江北区", "南岸区", "北碚区", "渝北区", "巴南区", "长寿区", "双桥区", "沙坪坝区", "万盛区", "万州区", "涪陵区", "黔江区", "永川区", "合川区", "江津区", "九龙坡区", "南川区", "綦江县", "潼南县", "荣昌县", "璧山县", "大足县", "铜梁县", "梁平县", "开县", "忠县", "城口县", "垫江县", "武隆县", "丰都县", "奉节县", "云阳县", "巫溪县", "巫山县", "石柱土家族自治县", "秀山土家族苗族自治县", "酉阳土家族苗族自治县", "彭水苗族土家族自治县", "其他" ] } ] }, { "name": "四川省", "city": [ { "name": "成都市", "area": [ "青羊区", "锦江区", "金牛区", "武侯区", "成华区", "龙泉驿区", "青白江区", "新都区", "温江区", "都江堰市", "彭州市", "邛崃市", "崇州市", "金堂县", "郫县", "新津县", "双流县", "蒲江县", "大邑县", "其他" ] }, { "name": "自贡市", "area": [ "大安区", "自流井区", "贡井区", "沿滩区", "荣县", "富顺县", "其他" ] }, { "name": "攀枝花市", "area": [ "仁和区", "米易县", "盐边县", "东区", "西区", "其他" ] }, { "name": "泸州市", "area": [ "江阳区", "纳溪区", "龙马潭区", "泸县", "合江县", "叙永县", "古蔺县", "其他" ] }, { "name": "德阳市", "area": [ "旌阳区", "广汉市", "什邡市", "绵竹市", "罗江县", "中江县", "其他" ] }, { "name": "绵阳市", "area": [ "涪城区", "游仙区", "江油市", "盐亭县", "三台县", "平武县", "安县", "梓潼县", "北川羌族自治县", "其他" ] }, { "name": "广元市", "area": [ "元坝区", "朝天区", "青川县", "旺苍县", "剑阁县", "苍溪县", "市中区", "其他" ] }, { "name": "遂宁市", "area": [ "船山区", "安居区", "射洪县", "蓬溪县", "大英县", "其他" ] }, { "name": "内江市", "area": [ "市中区", "东兴区", "资中县", "隆昌县", "威远县", "其他" ] }, { "name": "乐山市", "area": [ "市中区", "五通桥区", "沙湾区", "金口河区", "峨眉山市", "夹江县", "井研县", "犍为县", "沐川县", "马边彝族自治县", "峨边彝族自治县", "其他" ] }, { "name": "南充", "area": [ "顺庆区", "高坪区", "嘉陵区", "阆中市", "营山县", "蓬安县", "仪陇县", "南部县", "西充县", "其他" ] }, { "name": "眉山市", "area": [ "东坡区", "仁寿县", "彭山县", "洪雅县", "丹棱县", "青神县", "其他" ] }, { "name": "宜宾市", "area": [ "翠屏区", "宜宾县", "兴文县", "南溪县", "珙县", "长宁县", "高县", "江安县", "筠连县", "屏山县", "其他" ] }, { "name": "广安市", "area": [ "广安区", "华蓥市", "岳池县", "邻水县", "武胜县", "其他" ] }, { "name": "达州市", "area": [ "通川区", "万源市", "达县", "渠县", "宣汉县", "开江县", "大竹县", "其他" ] }, { "name": "雅安市", "area": [ "雨城区", "芦山县", "石棉县", "名山县", "天全县", "荥经县", "宝兴县", "汉源县", "其他" ] }, { "name": "巴中市", "area": [ "巴州区", "南江县", "平昌县", "通江县", "其他" ] }, { "name": "资阳市", "area": [ "雁江区", "简阳市", "安岳县", "乐至县", "其他" ] }, { "name": "阿坝藏族羌族自治州", "area": [ "马尔康县", "九寨沟县", "红原县", "汶川县", "阿坝县", "理县", "若尔盖县", "小金县", "黑水县", "金川县", "松潘县", "壤塘县", "茂县", "其他" ] }, { "name": "甘孜藏族自治州", "area": [ "康定县", "丹巴县", "炉霍县", "九龙县", "甘孜县", "雅江县", "新龙县", "道孚县", "白玉县", "理塘县", "德格县", "乡城县", "石渠县", "稻城县", "色达县", "巴塘县", "泸定县", "得荣县", "其他" ] }, { "name": "凉山彝族自治州", "area": [ "西昌市", "美姑县", "昭觉县", "金阳县", "甘洛县", "布拖县", "雷波县", "普格县", "宁南县", "喜德县", "会东县", "越西县", "会理县", "盐源县", "德昌县", "冕宁县", "木里藏族自治县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "贵州省", "city": [ { "name": "贵阳市", "area": [ "南明区", "云岩区", "花溪区", "乌当区", "白云区", "小河区", "清镇市", "开阳县", "修文县", "息烽县", "其他" ] }, { "name": "六盘水市", "area": [ "钟山区", "水城县", "盘县", "六枝特区", "其他" ] }, { "name": "遵义市", "area": [ "红花岗区", "汇川区", "赤水市", "仁怀市", "遵义县", "绥阳县", "桐梓县", "习水县", "凤冈县", "正安县", "余庆县", "湄潭县", "道真仡佬族苗族自治县", "务川仡佬族苗族自治县", "其他" ] }, { "name": "安顺市", "area": [ "西秀区", "普定县", "平坝县", "镇宁布依族苗族自治县", "紫云苗族布依族自治县", "关岭布依族苗族自治县", "其他" ] }, { "name": "铜仁地区", "area": [ "铜仁市", "德江县", "江口县", "思南县", "石阡县", "玉屏侗族自治县", "松桃苗族自治县", "印江土家族苗族自治县", "沿河土家族自治县", "万山特区", "其他" ] }, { "name": "毕节地区", "area": [ "毕节市", "黔西县", "大方县", "织金县", "金沙县", "赫章县", "纳雍县", "威宁彝族回族苗族自治县", "其他" ] }, { "name": "黔西南布依族苗族自治州", "area": [ "兴义市", "望谟县", "兴仁县", "普安县", "册亨县", "晴隆县", "贞丰县", "安龙县", "其他" ] }, { "name": "黔东南苗族侗族自治州", "area": [ "凯里市", "施秉县", "从江县", "锦屏县", "镇远县", "麻江县", "台江县", "天柱县", "黄平县", "榕江县", "剑河县", "三穗县", "雷山县", "黎平县", "岑巩县", "丹寨县", "其他" ] }, { "name": "黔南布依族苗族自治州", "area": [ "都匀市", "福泉市", "贵定县", "惠水县", "罗甸县", "瓮安县", "荔波县", "龙里县", "平塘县", "长顺县", "独山县", "三都水族自治县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "云南省", "city": [ { "name": "昆明市", "area": [ "盘龙区", "五华区", "官渡区", "西山区", "东川区", "安宁市", "呈贡县", "晋宁县", "富民县", "宜良县", "嵩明县", "石林彝族自治县", "禄劝彝族苗族自治县", "寻甸回族彝族自治县", "其他" ] }, { "name": "曲靖市", "area": [ "麒麟区", "宣威市", "马龙县", "沾益县", "富源县", "罗平县", "师宗县", "陆良县", "会泽县", "其他" ] }, { "name": "玉溪市", "area": [ "红塔区", "江川县", "澄江县", "通海县", "华宁县", "易门县", "峨山彝族自治县", "新平彝族傣族自治县", "元江哈尼族彝族傣族自治县", "其他" ] }, { "name": "保山市", "area": [ "隆阳区", "施甸县", "腾冲县", "龙陵县", "昌宁县", "其他" ] }, { "name": "昭通市", "area": [ "昭阳区", "鲁甸县", "巧家县", "盐津县", "大关县", "永善县", "绥江县", "镇雄县", "彝良县", "威信县", "水富县", "其他" ] }, { "name": "丽江市", "area": [ "古城区", "永胜县", "华坪县", "玉龙纳西族自治县", "宁蒗彝族自治县", "其他" ] }, { "name": "普洱市", "area": [ "思茅区", "普洱哈尼族彝族自治县", "墨江哈尼族自治县", "景东彝族自治县", "景谷傣族彝族自治县", "镇沅彝族哈尼族拉祜族自治县", "江城哈尼族彝族自治县", "孟连傣族拉祜族佤族自治县", "澜沧拉祜族自治县", "西盟佤族自治县", "其他" ] }, { "name": "临沧市", "area": [ "临翔区", "凤庆县", "云县", "永德县", "镇康县", "双江拉祜族佤族布朗族傣族自治县", "耿马傣族佤族自治县", "沧源佤族自治县", "其他" ] }, { "name": "德宏傣族景颇族自治州", "area": [ "潞西市", "瑞丽市", "梁河县", "盈江县", "陇川县", "其他" ] }, { "name": "怒江傈僳族自治州", "area": [ "泸水县", "福贡县", "贡山独龙族怒族自治县", "兰坪白族普米族自治县", "其他" ] }, { "name": "迪庆藏族自治州", "area": [ "香格里拉县", "德钦县", "维西傈僳族自治县", "其他" ] }, { "name": "大理白族自治州", "area": [ "大理市", "祥云县", "宾川县", "弥渡县", "永平县", "云龙县", "洱源县", "剑川县", "鹤庆县", "漾濞彝族自治县", "南涧彝族自治县", "巍山彝族回族自治县", "其他" ] }, { "name": "楚雄彝族自治州", "area": [ "楚雄市", "双柏县", "牟定县", "南华县", "姚安县", "大姚县", "永仁县", "元谋县", "武定县", "禄丰县", "其他" ] }, { "name": "红河哈尼族彝族自治州", "area": [ "蒙自县", "个旧市", "开远市", "绿春县", "建水县", "石屏县", "弥勒县", "泸西县", "元阳县", "红河县", "金平苗族瑶族傣族自治县", "河口瑶族自治县", "屏边苗族自治县", "其他" ] }, { "name": "文山壮族苗族自治州", "area": [ "文山县", "砚山县", "西畴县", "麻栗坡县", "马关县", "丘北县", "广南县", "富宁县", "其他" ] }, { "name": "西双版纳傣族自治州", "area": [ "景洪市", "勐海县", "勐腊县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "西藏", "city": [ { "name": "拉萨市", "area": [ "城关区", "林周县", "当雄县", "尼木县", "曲水县", "堆龙德庆县", "达孜县", "墨竹工卡县", "其他" ] }, { "name": "那曲地区", "area": [ "那曲县", "嘉黎县", "比如县", "聂荣县", "安多县", "申扎县", "索县", "班戈县", "巴青县", "尼玛县", "其他" ] }, { "name": "昌都地区", "area": [ "昌都县", "江达县", "贡觉县", "类乌齐县", "丁青县", "察雅县", "八宿县", "左贡县", "芒康县", "洛隆县", "边坝县", "其他" ] }, { "name": "林芝地区", "area": [ "林芝县", "工布江达县", "米林县", "墨脱县", "波密县", "察隅县", "朗县", "其他" ] }, { "name": "山南地区", "area": [ "乃东县", "扎囊县", "贡嘎县", "桑日县", "琼结县", "曲松县", "措美县", "洛扎县", "加查县", "隆子县", "错那县", "浪卡子县", "其他" ] }, { "name": "日喀则地区", "area": [ "日喀则市", "南木林县", "江孜县", "定日县", "萨迦县", "拉孜县", "昂仁县", "谢通门县", "白朗县", "仁布县", "康马县", "定结县", "仲巴县", "亚东县", "吉隆县", "聂拉木县", "萨嘎县", "岗巴县", "其他" ] }, { "name": "阿里地区", "area": [ "噶尔县", "普兰县", "札达县", "日土县", "革吉县", "改则县", "措勤县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "陕西省", "city": [ { "name": "西安市", "area": [ "莲湖区", "新城区", "碑林区", "雁塔区", "灞桥区", "未央区", "阎良区", "临潼区", "长安区", "高陵县", "蓝田县", "户县", "周至县", "其他" ] }, { "name": "铜川市", "area": [ "耀州区", "王益区", "印台区", "宜君县", "其他" ] }, { "name": "宝鸡市", "area": [ "渭滨区", "金台区", "陈仓区", "岐山县", "凤翔县", "陇县", "太白县", "麟游县", "扶风县", "千阳县", "眉县", "凤县", "其他" ] }, { "name": "咸阳市", "area": [ "秦都区", "渭城区", "杨陵区", "兴平市", "礼泉县", "泾阳县", "永寿县", "三原县", "彬县", "旬邑县", "长武县", "乾县", "武功县", "淳化县", "其他" ] }, { "name": "渭南市", "area": [ "临渭区", "韩城市", "华阴市", "蒲城县", "潼关县", "白水县", "澄城县", "华县", "合阳县", "富平县", "大荔县", "其他" ] }, { "name": "延安市", "area": [ "宝塔区", "安塞县", "洛川县", "子长县", "黄陵县", "延川县", "富县", "延长县", "甘泉县", "宜川县", "志丹县", "黄龙县", "吴起县", "其他" ] }, { "name": "汉中市", "area": [ "汉台区", "留坝县", "镇巴县", "城固县", "南郑县", "洋县", "宁强县", "佛坪县", "勉县", "西乡县", "略阳县", "其他" ] }, { "name": "榆林市", "area": [ "榆阳区", "清涧县", "绥德县", "神木县", "佳县", "府谷县", "子洲县", "靖边县", "横山县", "米脂县", "吴堡县", "定边县", "其他" ] }, { "name": "安康市", "area": [ "汉滨区", "紫阳县", "岚皋县", "旬阳县", "镇坪县", "平利县", "石泉县", "宁陕县", "白河县", "汉阴县", "其他" ] }, { "name": "商洛市", "area": [ "商州区", "镇安县", "山阳县", "洛南县", "商南县", "丹凤县", "柞水县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "甘肃省", "city": [ { "name": "兰州市", "area": [ "城关区", "七里河区", "西固区", "安宁区", "红古区", "永登县", "皋兰县", "榆中县", "其他" ] }, { "name": "嘉峪关市", "area": [ "嘉峪关市", "其他" ] }, { "name": "金昌市", "area": [ "金川区", "永昌县", "其他" ] }, { "name": "白银市", "area": [ "白银区", "平川区", "靖远县", "会宁县", "景泰县", "其他" ] }, { "name": "天水市", "area": [ "清水县", "秦安县", "甘谷县", "武山县", "张家川回族自治县", "北道区", "秦城区", "其他" ] }, { "name": "武威市", "area": [ "凉州区", "民勤县", "古浪县", "天祝藏族自治县", "其他" ] }, { "name": "酒泉市", "area": [ "肃州区", "玉门市", "敦煌市", "金塔县", "肃北蒙古族自治县", "阿克塞哈萨克族自治县", "安西县", "其他" ] }, { "name": "张掖市", "area": [ "甘州区", "民乐县", "临泽县", "高台县", "山丹县", "肃南裕固族自治县", "其他" ] }, { "name": "庆阳市", "area": [ "西峰区", "庆城县", "环县", "华池县", "合水县", "正宁县", "宁县", "镇原县", "其他" ] }, { "name": "平凉市", "area": [ "崆峒区", "泾川县", "灵台县", "崇信县", "华亭县", "庄浪县", "静宁县", "其他" ] }, { "name": "定西市", "area": [ "安定区", "通渭县", "临洮县", "漳县", "岷县", "渭源县", "陇西县", "其他" ] }, { "name": "陇南市", "area": [ "武都区", "成县", "宕昌县", "康县", "文县", "西和县", "礼县", "两当县", "徽县", "其他" ] }, { "name": "临夏回族自治州", "area": [ "临夏市", "临夏县", "康乐县", "永靖县", "广河县", "和政县", "东乡族自治县", "积石山保安族东乡族撒拉族自治县", "其他" ] }, { "name": "甘南藏族自治州", "area": [ "合作市", "临潭县", "卓尼县", "舟曲县", "迭部县", "玛曲县", "碌曲县", "夏河县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "青海省", "city": [ { "name": "西宁市", "area": [ "城中区", "城东区", "城西区", "城北区", "湟源县", "湟中县", "大通回族土族自治县", "其他" ] }, { "name": "海东地区", "area": [ "平安县", "乐都县", "民和回族土族自治县", "互助土族自治县", "化隆回族自治县", "循化撒拉族自治县", "其他" ] }, { "name": "海北藏族自治州", "area": [ "海晏县", "祁连县", "刚察县", "门源回族自治县", "其他" ] }, { "name": "海南藏族自治州", "area": [ "共和县", "同德县", "贵德县", "兴海县", "贵南县", "其他" ] }, { "name": "黄南藏族自治州", "area": [ "同仁县", "尖扎县", "泽库县", "河南蒙古族自治县", "其他" ] }, { "name": "果洛藏族自治州", "area": [ "玛沁县", "班玛县", "甘德县", "达日县", "久治县", "玛多县", "其他" ] }, { "name": "玉树藏族自治州", "area": [ "玉树县", "杂多县", "称多县", "治多县", "囊谦县", "曲麻莱县", "其他" ] }, { "name": "海西蒙古族藏族自治州", "area": [ "德令哈市", "格尔木市", "乌兰县", "都兰县", "天峻县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "宁夏", "city": [ { "name": "银川市", "area": [ "兴庆区", "西夏区", "金凤区", "灵武市", "永宁县", "贺兰县", "其他" ] }, { "name": "石嘴山市", "area": [ "大武口区", "惠农区", "平罗县", "其他" ] }, { "name": "吴忠市", "area": [ "利通区", "青铜峡市", "盐池县", "同心县", "其他" ] }, { "name": "固原市", "area": [ "原州区", "西吉县", "隆德县", "泾源县", "彭阳县", "其他" ] }, { "name": "中卫市", "area": [ "沙坡头区", "中宁县", "海原县", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "新疆", "city": [ { "name": "乌鲁木齐市", "area": [ "天山区", "沙依巴克区", "新市区", "水磨沟区", "头屯河区", "达坂城区", "东山区", "乌鲁木齐县", "其他" ] }, { "name": "克拉玛依市", "area": [ "克拉玛依区", "独山子区", "白碱滩区", "乌尔禾区", "其他" ] }, { "name": "吐鲁番地区", "area": [ "吐鲁番市", "托克逊县", "鄯善县", "其他" ] }, { "name": "哈密地区", "area": [ "哈密市", "伊吾县", "巴里坤哈萨克自治县", "其他" ] }, { "name": "和田地区", "area": [ "和田市", "和田县", "洛浦县", "民丰县", "皮山县", "策勒县", "于田县", "墨玉县", "其他" ] }, { "name": "阿克苏地区", "area": [ "阿克苏市", "温宿县", "沙雅县", "拜城县", "阿瓦提县", "库车县", "柯坪县", "新和县", "乌什县", "其他" ] }, { "name": "喀什地区", "area": [ "喀什市", "巴楚县", "泽普县", "伽师县", "叶城县", "岳普湖县", "疏勒县", "麦盖提县", "英吉沙县", "莎车县", "疏附县", "塔什库尔干塔吉克自治县", "其他" ] }, { "name": "克孜勒苏柯尔克孜自治州", "area": [ "阿图什市", "阿合奇县", "乌恰县", "阿克陶县", "其他" ] }, { "name": "巴音郭楞蒙古自治州", "area": [ "库尔勒市", "和静县", "尉犁县", "和硕县", "且末县", "博湖县", "轮台县", "若羌县", "焉耆回族自治县", "其他" ] }, { "name": "昌吉回族自治州", "area": [ "昌吉市", "阜康市", "奇台县", "玛纳斯县", "吉木萨尔县", "呼图壁县", "木垒哈萨克自治县", "米泉市", "其他" ] }, { "name": "博尔塔拉蒙古自治州", "area": [ "博乐市", "精河县", "温泉县", "其他" ] }, { "name": "石河子", "area": [ "石河子" ] }, { "name": "阿拉尔", "area": [ "阿拉尔" ] }, { "name": "图木舒克", "area": [ "图木舒克" ] }, { "name": "五家渠", "area": [ "五家渠" ] }, { "name": "伊犁哈萨克自治州", "area": [ "伊宁市", "奎屯市", "伊宁县", "特克斯县", "尼勒克县", "昭苏县", "新源县", "霍城县", "巩留县", "察布查尔锡伯自治县", "塔城地区", "阿勒泰地区", "其他" ] }, { "name": "其他", "area": [ "其他" ] } ] }, { "name": "台湾省", "city": [ { "name": "台北市", "area": [ "内湖区", "南港区", "中正区", "万华区", "大同区", "中山区", "松山区", "大安区", "信义区", "文山区", "士林区", "北投区" ] }, { "name": "新北市", "area": [ "板桥区", "汐止区", "新店区", "其他" ] }, { "name": "桃园市", "area": [ "其他" ] }, { "name": "台中市", "area": [ "其他" ] }, { "name": "台南市", "area": [ "其他" ] }, { "name": "高雄市", "area": [ "其他" ] } ] }, { "name": "澳门", "city": [ { "name": "澳门", "area": [ "花地玛堂区", "圣安多尼堂区", "大堂区", "望德堂区", "风顺堂区", "嘉模堂区", "圣方济各堂区", "路凼", "其他" ] } ] }, { "name": "香港", "city": [ { "name": "香港", "area": [ "深水埗区", "油尖旺区", "九龙城区", "黄大仙区", "观塘区", "北区", "大埔区", "沙田区", "西贡区", "元朗区", "屯门区", "荃湾区", "葵青区", "离岛区", "中西区", "湾仔区", "东区", "南区", "其他" ] } ] } ] ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/BaseSearch.java ================================================ package top.flya.system.utils.sensitivewordsfiliter; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; public class BaseSearch { protected TrieNode2[] _first = new TrieNode2[Character.MAX_VALUE + 1]; protected String[] _keywords; /** * 设置关键字 * * @param keywords 关键字列表 */ public void SetKeywords(List keywords) { _keywords = new String[keywords.size()]; _keywords = keywords.toArray(_keywords); SetKeywords(); } protected void SetKeywords() { TrieNode root = new TrieNode(); Map> allNodeLayers = new Hashtable>(); for (int i = 0; i < _keywords.length; i++) { String p = _keywords[i]; TrieNode nd = root; for (int j = 0; j < p.length(); j++) { nd = nd.Add(p.charAt(j)); if (nd.Layer == 0) { nd.Layer = j + 1; if (allNodeLayers.containsKey(nd.Layer) == false) { List nodes = new ArrayList(); nodes.add(nd); allNodeLayers.put(nd.Layer, nodes); } else { allNodeLayers.get(nd.Layer).add(nd); } } } nd.SetResults(i); } List allNode = new ArrayList(); allNode.add(root); for (int i = 0; i < allNodeLayers.size(); i++) { // 注意 这里不能用 keySet() List nodes = allNodeLayers.get(i + 1); for (int j = 0; j < nodes.size(); j++) { allNode.add(nodes.get(j)); } } allNodeLayers.clear(); allNodeLayers = null; for (int i = 1; i < allNode.size(); i++) { TrieNode nd = allNode.get(i); nd.Index = i; TrieNode r = nd.Parent.Failure; Character c = nd.Char; while (r != null && !r.m_values.containsKey(c)) r = r.Failure; if (r == null) nd.Failure = root; else { nd.Failure = r.m_values.get(c); for (Integer result : nd.Failure.Results) { nd.SetResults(result); } } } root.Failure = root; List allNode2 = new ArrayList(); for (int i = 0; i < allNode.size(); i++) { allNode2.add(new TrieNode2()); } for (int i = 0; i < allNode2.size(); i++) { TrieNode oldNode = allNode.get(i); TrieNode2 newNode = allNode2.get(i); for (Character key : oldNode.m_values.keySet()) { TrieNode nd = oldNode.m_values.get(key); newNode.Add(key, allNode2.get(nd.Index)); } oldNode.Results.forEach(item -> { newNode.SetResults(item); }); oldNode = oldNode.Failure; while (oldNode != root) { for (Character key : oldNode.m_values.keySet()) { TrieNode nd = oldNode.m_values.get(key); if (newNode.HasKey(key) == false) { newNode.Add(key, allNode2.get(nd.Index)); } } oldNode.Results.forEach(item -> { newNode.SetResults(item); }); oldNode = oldNode.Failure; } } allNode.clear(); allNode = null; root = null; TrieNode2[] first = new TrieNode2[Character.MAX_VALUE + 1]; TrieNode2 root2 = allNode2.get(0); for (Character key : root2.m_values.keySet()) { TrieNode2 nd = root2.m_values.get(key); first[(int) key] = nd; } _first = first; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/TrieNode.java ================================================ package top.flya.system.utils.sensitivewordsfiliter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class TrieNode implements Comparable { public int Index; public int Layer; public boolean End; public char Char; public List Results; public HashMap m_values; public TrieNode Failure; public TrieNode Parent; public boolean IsWildcard; public int WildcardLayer; public boolean HasWildcard; public TrieNode() { m_values = new HashMap(); Results = new ArrayList(); } public TrieNode Add(final Character c) { if (m_values.containsKey(c)) { return m_values.get(c); } final TrieNode node = new TrieNode(); node.Parent = this; node.Char = c; m_values.put(c, node); return node; } public void SetResults(final Integer index) { if (End == false) { End = true; } if (Results.contains(index) == false) { Results.add(index); } } @Override public int compareTo(final TrieNode o) { return this.Layer - o.Layer ; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/TrieNode2.java ================================================ package top.flya.system.utils.sensitivewordsfiliter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class TrieNode2 { public boolean End; public List Results; public HashMap m_values; private int minflag = Integer.MAX_VALUE; private int maxflag = 0; public TrieNode2() { Results = new ArrayList(); m_values = new HashMap(); } public void Add(final char c, final TrieNode2 node3) { if (minflag > c) { minflag = c; } if (maxflag < c) { maxflag = c; } m_values.put(c, node3); } public void SetResults(final Integer index) { if (End == false) { End = true; } if (Results.contains(index) == false) { Results.add(index); } } public boolean HasKey(final char c) { if (minflag <= c && maxflag >= c) { return m_values.containsKey(c); } return false; } public TrieNode2 GetValue(final char c) { return m_values.get(c); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/WordsSearch.java ================================================ package top.flya.system.utils.sensitivewordsfiliter; import java.util.ArrayList; import java.util.List; public class WordsSearch extends BaseSearch { public String[] _others; /** * 在文本中查找第一个关键字 * * @param text 文本 * @return */ public WordsSearchResult FindFirst(final String text) { TrieNode2 ptr = null; for (int i = 0; i < text.length(); i++) { final char t = text.charAt(i); TrieNode2 tn = null; if (ptr == null) { tn = _first[t]; } else { if (ptr.HasKey(t) == false) { tn = _first[t]; } else { tn = ptr.GetValue(t); } } if (tn != null) { if (tn.End) { for (final Integer index : tn.Results) { final String key = _keywords[index]; return new WordsSearchResult(key, i + 1 - key.length(), i, index); } } } ptr = tn; } return null; } /** * 在文本中查找所有的关键字 * * @param text 文本 * @return */ public List FindAll(final String text) { TrieNode2 ptr = null; final List list = new ArrayList(); for (int i = 0; i < text.length(); i++) { final char t = text.charAt(i); TrieNode2 tn = null; if (ptr == null) { tn = _first[t]; } else { if (ptr.HasKey(t) == false) { tn = _first[t]; } else { tn = ptr.GetValue(t); } } if (tn != null) { if (tn.End) { for (final Integer index : tn.Results) { final String key = _keywords[index]; final WordsSearchResult item = new WordsSearchResult(key, i + 1 - key.length(), i, index); list.add(item); } } } ptr = tn; } return list; } /** * 判断文本是否包含关键字 * * @param text 文本 * @return */ public boolean ContainsAny(final String text) { TrieNode2 ptr = null; for (int i = 0; i < text.length(); i++) { final char t = text.charAt(i); TrieNode2 tn = null; if (ptr == null) { tn = _first[t]; } else { if (ptr.HasKey(t) == false) { tn = _first[t]; } else { tn = ptr.GetValue(t); } } if (tn != null) { if (tn.End) { return true; } } ptr = tn; } return false; } /** * 在文本中替换所有的关键字, 替换符默认为 * * * @param text 文本 * @return */ public String Replace(final String text) { return Replace(text, '*'); } /** * 在文本中替换所有的关键字 * * @param text 文本 * @param replaceChar 替换符 * @return */ public String Replace(final String text, final char replaceChar) { final StringBuilder result = new StringBuilder(text); TrieNode2 ptr = null; for (int i = 0; i < text.length(); i++) { final char t = text.charAt(i); TrieNode2 tn = null; if (ptr == null) { tn = _first[t]; } else { if (ptr.HasKey(t) == false) { tn = _first[t]; } else { tn = ptr.GetValue(t); } } if (tn != null) { if (tn.End) { final int maxLength = _keywords[tn.Results.get(0)].length(); final int start = i + 1 - maxLength; for (int j = start; j <= i; j++) { result.setCharAt(j, replaceChar); } } } ptr = tn; } return result.toString(); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/WordsSearchResult.java ================================================ package top.flya.system.utils.sensitivewordsfiliter; public class WordsSearchResult { public WordsSearchResult(final String keyword, final int start, final int end, final int index) { Keyword = keyword; End = end; Start = start; Index = index; MatchKeyword = keyword; } public WordsSearchResult(final String keyword, final int start, final int end, final int index, final String matchKeyword) { Keyword = keyword; End = end; Start = start; Index = index; MatchKeyword = matchKeyword; } /** 开始位置 */ public int Start; /** 结束位置 */ public int End; /** 关键字 */ public String Keyword; /** 索引 */ public int Index; /** 匹配关键字 */ public String MatchKeyword; } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/utils/sensitivewordsfiliter/WorldsFilterUtils.java ================================================ package top.flya.system.utils.sensitivewordsfiliter; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import java.io.File; import java.util.ArrayList; import java.util.List; /** * User:azu * Date: 2021/5/19 * Time:16:29 */ @Slf4j public class WorldsFilterUtils { private static final List blackWordsList = new ArrayList<>(); public static void initBlackWordsList() { try { Resource resource = new ClassPathResource("wordsfilter/sensi_words2.txt"); if (!resource.exists()) { log.error("未找到黑词文件库"); return; } File file = FileUtil.writeFromStream(resource.getInputStream(), FileUtil.createTempFile()); List keyList = FileUtil.readUtf8Lines(file); blackWordsList.addAll(keyList); log.info("加载黑词库行数 => {}", keyList.size()); } catch (Exception e) { log.error("黑词库加载失败", e); } } private static boolean wordsCheck(List worlds, String content) { WordsSearch iwords = new WordsSearch(); iwords.SetKeywords(worlds); return iwords.ContainsAny(content); } public static boolean checkBySystemWords(String content) { loadSystemKeywords(); return wordsCheck(blackWordsList, content); } public synchronized static void loadSystemKeywords() { if (!blackWordsList.isEmpty()) { return; } initBlackWordsList(); } public synchronized static void forceLoadSystemKeywords() { blackWordsList.clear(); initBlackWordsList(); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/xxlJob/ScheduledExecutorUtils.java ================================================ package top.flya.system.xxlJob; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledExecutorUtils { // 定义一个方法用于安排定时任务,同时接受传入的参数 public static void scheduleTask(String timeString, RunnableWithParams task, Object... params) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date targetTime = sdf.parse(timeString); long initialDelay = targetTime.getTime() - System.currentTimeMillis(); ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // 创建一个任务实例,可以将传入的参数传递给任务逻辑 Runnable runnable = () -> { task.run(params); }; scheduler.schedule(runnable, initialDelay, TimeUnit.MILLISECONDS); } public static void main(String[] args) { try { String targetTimeString = "2023-08-20 15:30:00"; // Change this to your desired time // 创建一个任务逻辑,接受参数并在任务执行时使用 RunnableWithParams task = (params) -> { System.out.println("Task executed at: " + new Date()); if (params.length > 0) { System.out.println("Parameter received: " + params[0]); } // 在这里添加你的任务逻辑 }; String parameter = "Hello, World!"; // 传递给任务逻辑的参数 // 调用方法安排定时任务,并传递参数 scheduleTask(targetTimeString, task, parameter); System.out.println("Scheduled task for: " + targetTimeString); } catch (ParseException e) { e.printStackTrace(); } } // 定义一个函数式接口,允许任务逻辑接受参数 @FunctionalInterface public interface RunnableWithParams { void run(Object... params); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/xxlJob/WxHandler.java ================================================ package top.flya.system.xxlJob; import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import top.flya.system.domain.PzcUser; import top.flya.system.mapper.PzcUserMapper; import javax.annotation.Resource; import java.util.List; @Component @Slf4j public class WxHandler { @Resource private PzcUserMapper pzcUserMapper; // /** // * 1、简单任务示例(Bean模式) // */ // @XxlJob("demoJobHandler") // public void demoJobHandler() throws Exception { // XxlJobHelper.log("XXL-JOB, Hello World."); // // for (int i = 0; i < 5; i++) { // XxlJobHelper.log("beat at:" + i); // } // // default success // } /** * 每天的0点自动同步 用户可以取消的次数取决于用户当前等级 */ @XxlJob("wxJobHandler") public void syncWxUserCancel() { XxlJobHelper.log("定时同步微信用户 的 取消活动次数 FL1906"); log.info("定时同步微信用户 的 取消活动次数"); List pzcUsers = pzcUserMapper.selectList(); for (PzcUser pzcUser : pzcUsers) { pzcUser.setUserLevel(Long.valueOf(getLevel(Math.toIntExact(pzcUser.getIntegration())))); pzcUser.setExemptCancel(Math.toIntExact(pzcUser.getUserLevel())); pzcUserMapper.updateById(pzcUser); } log.info("定时同步微信用户 的 取消活动次数 完成"); XxlJobHelper.log("定时同步微信用户 的 取消活动次数 完成 FL1906"); } public Integer getLevel(Integer jf) { if (jf <= 30) { return 1; } if (jf <= 70) { return 2; } if (jf <= 120) { return 3; } if (jf <= 180) { return 4; } else { return 5; } } /** * 每天的 1点自动同步积分 与取消次数 与用户等级 的映射关系 (用户升级) TODO 这个好像用不上 直接在订单处 进行 */ } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/JobLoginService.java ================================================ package top.flya.system.xxlJob.diy; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.net.HttpCookie; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @Component public class JobLoginService { private final Map loginCookie=new HashMap<>(); @Value("${xxl.job.admin.address}") private String adminAddresses; @Value("${xxl.job.admin.username}") private String username; @Value("${xxl.job.admin.password}") private String password; public void login() { String url=adminAddresses+"/login"; HttpResponse response = HttpRequest.post(url) .form("userName",username) .form("password",password) .execute(); List cookies = response.getCookies(); Optional cookieOpt = cookies.stream() .filter(cookie -> cookie.getName().equals("XXL_JOB_LOGIN_IDENTITY")).findFirst(); if (!cookieOpt.isPresent()) throw new RuntimeException("get xxl-job cookie error!"); String value = cookieOpt.get().getValue(); loginCookie.put("XXL_JOB_LOGIN_IDENTITY",value); } public String getCookie() { for (int i = 0; i < 3; i++) { String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY"); //redis if (cookieStr !=null) { return "XXL_JOB_LOGIN_IDENTITY="+cookieStr; } login(); } throw new RuntimeException("get xxl-job cookie error!"); } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/TestController.java ================================================ package top.flya.system.xxlJob.diy; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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 top.flya.common.core.domain.R; import java.util.List; import java.util.stream.Collectors; @RestController @RequestMapping("/test") public class TestController { @Value("${xxl.job.admin.url.add}") private String addUrl; @Value("${xxl.job.admin.url.pageList}") public String pageListUrl; @Value("${xxl.job.admin.url.update}") public String updateUrl; @Value("${xxl.job.admin.url.start}") public String startUrl; @Value("${xxl.job.admin.url.stop}") public String stopUrl; @Autowired private JobLoginService jobLoginService; @GetMapping("/list") public List getJobList() { HttpResponse response = HttpRequest.post(pageListUrl) .form("jobGroup", 3) .form("triggerStatus", -1) .form("jobDesc", "") .form("executorHandler", "wxHandler") .form("start", 0) .form("length", 10) .form("author", "") .cookie(jobLoginService.getCookie()) .execute(); String body = response.body(); JSONArray array = JSONUtil.parse(body).getByPath("data", JSONArray.class); List list = array.stream() .map(o -> JSONUtil.toBean((JSONObject) o, XxlJobInfo.class)) .collect(Collectors.toList()); return list; } @PostMapping("/add") public R createNewJob() { HttpResponse response = HttpRequest.post(addUrl) .form("jobGroup", 3) .form("jobDesc", "测试任务2333") .form("author", "flya") .form("alarmEmail", "") .form("scheduleType", "CRON") .form("scheduleConf", "0 0 0 * * ?") .form("cronGen_display", "0 0 0 * * ?") .form("schedule_conf_CRON","") .form("schedule_conf_FIX_RATE","") .form("schedule_conf_FIX_DELAY","") .form("glueType", "BEAN") .form("executorHandler", "wxHandler") .form("executorParam", "") .form("executorRouteStrategy", "FIRST") .form("childJobId", "") .form("misfireStrategy", "DO_NOTHING") .form("executorBlockStrategy", "SERIAL_EXECUTION") .form("executorTimeout", 0) .form("executorFailRetryCount", 0) .form("glueRemark", "GLUE代码初始化") .form("glueSource", "") .cookie(jobLoginService.getCookie()) .execute(); String body = response.body(); JSONObject jsonObject = JSONUtil.parseObj(body); if (jsonObject.getInt("code") == 200) { return R.ok(); }else { return R.fail(); } } @PostMapping("/update") public R updateNewJob() { HttpResponse response = HttpRequest.post(updateUrl) .form("jobGroup", 3) .form("jobDesc", "测试任务") .form("author", "flya2333") .form("alarmEmail", "") .form("scheduleType", "CRON") .form("scheduleConf", "0 0 0 * * ?") .form("cronGen_display", "0 0 0 * * ?") .form("schedule_conf_CRON","") .form("schedule_conf_FIX_RATE","") .form("schedule_conf_FIX_DELAY","") .form("glueType", "BEAN") .form("executorHandler", "wxHandler") .form("executorParam", "") .form("executorRouteStrategy", "FIRST") .form("childJobId", "") .form("misfireStrategy", "DO_NOTHING") .form("executorBlockStrategy", "SERIAL_EXECUTION") .form("executorTimeout", 0) .form("executorFailRetryCount", 0) .form("glueRemark", "GLUE代码初始化") .form("glueSource", "") .form("id", 5) .cookie(jobLoginService.getCookie()) .execute(); String body = response.body(); JSONObject jsonObject = JSONUtil.parseObj(body); if (jsonObject.getInt("code") == 200) { return R.ok(); }else { return R.fail(); } } @PostMapping("/start") public R startNewJob() { HttpResponse response = HttpRequest.post(startUrl+"?id=5") .cookie(jobLoginService.getCookie()) .execute(); String body = response.body(); JSONObject jsonObject = JSONUtil.parseObj(body); if (jsonObject.getInt("code") == 200) { return R.ok(); }else { return R.fail(); } } @PostMapping("/stop") public R stopNewJob() { HttpResponse response = HttpRequest.post(stopUrl+"?id=5") .cookie(jobLoginService.getCookie()) .execute(); String body = response.body(); JSONObject jsonObject = JSONUtil.parseObj(body); if (jsonObject.getInt("code") == 200) { return R.ok(); }else { return R.fail(); } } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/XxlJobGroup.java ================================================ package top.flya.system.xxlJob.diy; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; /** * Created by xuxueli on 16/9/30. */ public class XxlJobGroup { private int id; private String appname; private String title; private int addressType; // 执行器地址类型:0=自动注册、1=手动录入 private String addressList; // 执行器地址列表,多地址逗号分隔(手动录入) private Date updateTime; // registry list private List registryList; // 执行器地址列表(系统注册) public List getRegistryList() { if (addressList!=null && addressList.trim().length()>0) { registryList = new ArrayList(Arrays.asList(addressList.split(","))); } return registryList; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAppname() { return appname; } public void setAppname(String appname) { this.appname = appname; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getAddressType() { return addressType; } public void setAddressType(int addressType) { this.addressType = addressType; } public String getAddressList() { return addressList; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public void setAddressList(String addressList) { this.addressList = addressList; } } ================================================ FILE: PaiZhiCheng/src/main/java/top/flya/system/xxlJob/diy/XxlJobInfo.java ================================================ package top.flya.system.xxlJob.diy; import java.util.Date; /** * xxl-job info * * @author xuxueli 2016-1-12 18:25:49 */ public class XxlJobInfo { private int id; // 主键ID private int jobGroup; // 执行器主键ID private String jobDesc; private Date addTime; private Date updateTime; private String author; // 负责人 private String alarmEmail; // 报警邮件 private String scheduleType; // 调度类型 private String scheduleConf; // 调度配置,值含义取决于调度类型 private String misfireStrategy; // 调度过期策略 private String executorRouteStrategy; // 执行器路由策略 private String executorHandler; // 执行器,任务Handler名称 private String executorParam; // 执行器,任务参数 private String executorBlockStrategy; // 阻塞处理策略 private int executorTimeout; // 任务执行超时时间,单位秒 private int executorFailRetryCount; // 失败重试次数 private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum private String glueSource; // GLUE源代码 private String glueRemark; // GLUE备注 private Date glueUpdatetime; // GLUE更新时间 private String childJobId; // 子任务ID,多个逗号分隔 private int triggerStatus; // 调度状态:0-停止,1-运行 private long triggerLastTime; // 上次调度时间 private long triggerNextTime; // 下次调度时间 public int getId() { return id; } public void setId(int id) { this.id = id; } public int getJobGroup() { return jobGroup; } public void setJobGroup(int jobGroup) { this.jobGroup = jobGroup; } public String getJobDesc() { return jobDesc; } public void setJobDesc(String jobDesc) { this.jobDesc = jobDesc; } public Date getAddTime() { return addTime; } public void setAddTime(Date addTime) { this.addTime = addTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getAlarmEmail() { return alarmEmail; } public void setAlarmEmail(String alarmEmail) { this.alarmEmail = alarmEmail; } public String getScheduleType() { return scheduleType; } public void setScheduleType(String scheduleType) { this.scheduleType = scheduleType; } public String getScheduleConf() { return scheduleConf; } public void setScheduleConf(String scheduleConf) { this.scheduleConf = scheduleConf; } public String getMisfireStrategy() { return misfireStrategy; } public void setMisfireStrategy(String misfireStrategy) { this.misfireStrategy = misfireStrategy; } public String getExecutorRouteStrategy() { return executorRouteStrategy; } public void setExecutorRouteStrategy(String executorRouteStrategy) { this.executorRouteStrategy = executorRouteStrategy; } public String getExecutorHandler() { return executorHandler; } public void setExecutorHandler(String executorHandler) { this.executorHandler = executorHandler; } public String getExecutorParam() { return executorParam; } public void setExecutorParam(String executorParam) { this.executorParam = executorParam; } public String getExecutorBlockStrategy() { return executorBlockStrategy; } public void setExecutorBlockStrategy(String executorBlockStrategy) { this.executorBlockStrategy = executorBlockStrategy; } public int getExecutorTimeout() { return executorTimeout; } public void setExecutorTimeout(int executorTimeout) { this.executorTimeout = executorTimeout; } public int getExecutorFailRetryCount() { return executorFailRetryCount; } public void setExecutorFailRetryCount(int executorFailRetryCount) { this.executorFailRetryCount = executorFailRetryCount; } public String getGlueType() { return glueType; } public void setGlueType(String glueType) { this.glueType = glueType; } public String getGlueSource() { return glueSource; } public void setGlueSource(String glueSource) { this.glueSource = glueSource; } public String getGlueRemark() { return glueRemark; } public void setGlueRemark(String glueRemark) { this.glueRemark = glueRemark; } public Date getGlueUpdatetime() { return glueUpdatetime; } public void setGlueUpdatetime(Date glueUpdatetime) { this.glueUpdatetime = glueUpdatetime; } public String getChildJobId() { return childJobId; } public void setChildJobId(String childJobId) { this.childJobId = childJobId; } public int getTriggerStatus() { return triggerStatus; } public void setTriggerStatus(int triggerStatus) { this.triggerStatus = triggerStatus; } public long getTriggerLastTime() { return triggerLastTime; } public void setTriggerLastTime(long triggerLastTime) { this.triggerLastTime = triggerLastTime; } public long getTriggerNextTime() { return triggerNextTime; } public void setTriggerNextTime(long triggerNextTime) { this.triggerNextTime = triggerNextTime; } } ================================================ FILE: PaiZhiCheng/src/main/resources/apiclient_key.pem ================================================ -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJKRICZHhIi47i QWwEsWjtVA2TBgtXIhrprdKn64MYZGHHAoISi3vPnUwd9RDdjRaxHr0yf13qO4zx MRiky3GWYEiic769MQKmJZubk72hrB3u4/CRhaSxj5XTJfUy8v95/ILB3yravamA tfan95xNyplBhq1eF4JHIJfBKHCrZ6jN1P7/ERzsR97bVZ0vS1uPQQYWZ+Hy9nC6 vsCe4c0EEldKDX5uNZ48KkIJnMVhXinf7Jv/NzIEZiPU+Cd6rSF/7kn33E1CkFah KYAX6KutOFIJcuquyvHsDgEWbYqMhoEXm6QpE+g1yh1xEs+KPhBkbMFxHyEkSFsM QiPJkYyHAgMBAAECggEANmfKPCVqNsyv91eUXGyTIWUTSsletdE+kCb4C3xx913m 6Akwns1kzhEP7iZCynkHQx46M3cpMlmq0+zgammvrekam/1MACVKPx96x4gcyKYh bmPtw53unitkbTgd6gq4uAhoYQD3uTOErZAJRwJ8HroF2ygOqZ0YGh6hjJdgaarj HG9vnRD7Q2CSOnxNXKqExbGW8Qjwj+MT2x/ecu7cCXqhvV7SSyRwGPA8HJ9WfjP1 +SOs502hrdZUPpyGVPUZdl5Pv/zsl+dCipqgAqyuJX6Y3rzoVH1XGANLhXDikQdQ gQpFg0rCigf7HmVdNk1xbb5kn6CuNYt0Lpd2zGAcEQKBgQD73bILtaMntUZ2FCmS 7ZNrv/0LANQupmECoLjdelHkSPVFROzv1MAw+9rD7Gzcmk3qcUrIGGlbAzD9phDc lW2Co0mnzUlVjhSOE4h1o96bEhlk0DTsNkG+mP418IpMaC6wzjVpVIOK+sNybE78 oC4Kd5DldMFbNdcNPIeMIhbRXwKBgQDMdlFA5UYmBs/gO/bDMr4y2bPYnVPIM1QN BzU5BeU88UPCe6qMk/W4KTYoutMctNR7CVW6RAof64dbWSqrXfEAfS3PaVj4lqk+ QA3Q0mYrcDNFeqvupnCuuCRLcqj+mDSbz4TmA3wh1H0Rjr7GSBc4Nm70aI2ybibA 9r6D5g/N2QKBgQDi+l67CLz5Svct8Guq+qlxYDq1kNCnHc+tI5SWG+bzGQDYpQ31 8MAnJMF48XcFs5VmIyUmgEFqAM1EuUTW1V80bN0y+OEO1hUWGOpQQhaZn0z9OlmH SjojfxMRHy4zP2xcb+lYfA5z0BsU4iCor93uFuSgtICQJ0wfpJ3vHsV7dwKBgB0G Apy6rw2A1AtZl7q3vkYLnzr1gkod9yVuS4DPtG3FNcAqu9f+vNeqifSYKJWfmbXp alDpjaJgVbOC4cq3qBlQq6sQoj+Pa3DZuNxWsYgjAjQvqK5U1BQJMaXAHfsd8gHY IF5iSkGnHyXZ5HzTCPDC0VdCbDLS7g9gN0UT6FRpAoGAC8GKrdjDwMvkUtbA3aGy 99FnuD9oqaTLdcFo0O5xVNdqNK+yRf4yY99ky8RqfxJ/I1vPyU1hoviB2qYKphp1 HDtbu6gz4/kuosiRh8vWi2/kHREAyw20iRbRncBEmxLJuKKxNlbz5vF76RzlSTTg A5zHu1EygULI4ytgxx5lTEQ= -----END PRIVATE KEY----- ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcActivityConnArtistMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcActivityConnIntroMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcActivityConnTagMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcActivityGroupApplyMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcActivityGroupMapper.xml ================================================ select group_id,activity_id,g.user_id as user_id,title,g.money as money,u.money as u_money,group_type,g.address as address,activity_time,auth,g.create_time as create_time,g.update_time as update_time, openid,user_level,nickname,sex,avatar,u.address as u_address,intro,age,constellation,mbti,hobby,school,occupation,music_style,region ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcActivityMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcArtistMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcIntroMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcOfficialMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcOrderMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcOrganizerMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcOrganizerTicketMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcRegionMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcTagMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcUserCollectMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcUserHistoryMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcUserMapper.xml ================================================ update pzc_user set money = #{bo.money} where user_id = #{bo.userId} ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcUserPhotoMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcUserTalkMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/mapper/system/PzcViewPagerMapper.xml ================================================ ================================================ FILE: PaiZhiCheng/src/main/resources/static/bootstrap.css ================================================ /*! * Bootstrap v2.0.4 * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. */ article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } audio:not([controls]) { display: none; } html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } a:hover, a:active { outline: 0; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { max-width: 100%; vertical-align: middle; border: 0; -ms-interpolation-mode: bicubic; } #map_canvas img { max-width: none; } button, input, select, textarea { margin: 0; font-size: 100%; vertical-align: middle; } button, input { *overflow: visible; line-height: normal; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; } textarea { overflow: auto; vertical-align: top; } .clearfix { *zoom: 1; } .clearfix:before, .clearfix:after { display: table; content: ""; } .clearfix:after { clear: both; } .hide-text { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .input-block-level { display: block; width: 100%; min-height: 28px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; } body { margin: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px; color: #333333; background-color: #ffffff; } a { color: #0088cc; text-decoration: none; } a:hover { color: #005580; text-decoration: underline; } .row { margin-left: -20px; *zoom: 1; } .row:before, .row:after { display: table; content: ""; } .row:after { clear: both; } [class*="span"] { float: left; margin-left: 20px; } .container, .navbar-fixed-top .container, .navbar-fixed-bottom .container { width: 940px; } .span12 { width: 940px; } .span11 { width: 860px; } .span10 { width: 780px; } .span9 { width: 700px; } .span8 { width: 620px; } .span7 { width: 540px; } .span6 { width: 460px; } .span5 { width: 380px; } .span4 { width: 300px; } .span3 { width: 220px; } .span2 { width: 140px; } .span1 { width: 60px; } .offset12 { margin-left: 980px; } .offset11 { margin-left: 900px; } .offset10 { margin-left: 820px; } .offset9 { margin-left: 740px; } .offset8 { margin-left: 660px; } .offset7 { margin-left: 580px; } .offset6 { margin-left: 500px; } .offset5 { margin-left: 420px; } .offset4 { margin-left: 340px; } .offset3 { margin-left: 260px; } .offset2 { margin-left: 180px; } .offset1 { margin-left: 100px; } .row-fluid { width: 100%; *zoom: 1; } .row-fluid:before, .row-fluid:after { display: table; content: ""; } .row-fluid:after { clear: both; } .row-fluid [class*="span"] { display: block; float: left; width: 100%; min-height: 28px; margin-left: 2.127659574%; *margin-left: 2.0744680846382977%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; } .row-fluid [class*="span"]:first-child { margin-left: 0; } .row-fluid .span12 { width: 99.99999998999999%; *width: 99.94680850063828%; } .row-fluid .span11 { width: 91.489361693%; *width: 91.4361702036383%; } .row-fluid .span10 { width: 82.97872339599999%; *width: 82.92553190663828%; } .row-fluid .span9 { width: 74.468085099%; *width: 74.4148936096383%; } .row-fluid .span8 { width: 65.95744680199999%; *width: 65.90425531263828%; } .row-fluid .span7 { width: 57.446808505%; *width: 57.3936170156383%; } .row-fluid .span6 { width: 48.93617020799999%; *width: 48.88297871863829%; } .row-fluid .span5 { width: 40.425531911%; *width: 40.3723404216383%; } .row-fluid .span4 { width: 31.914893614%; *width: 31.8617021246383%; } .row-fluid .span3 { width: 23.404255317%; *width: 23.3510638276383%; } .row-fluid .span2 { width: 14.89361702%; *width: 14.8404255306383%; } .row-fluid .span1 { width: 6.382978723%; *width: 6.329787233638298%; } .container { margin-right: auto; margin-left: auto; *zoom: 1; } .container:before, .container:after { display: table; content: ""; } .container:after { clear: both; } .container-fluid { padding-right: 20px; padding-left: 20px; *zoom: 1; } .container-fluid:before, .container-fluid:after { display: table; content: ""; } .container-fluid:after { clear: both; } p { margin: 0 0 9px; } p small { font-size: 11px; color: #999999; } .lead { margin-bottom: 18px; font-size: 20px; font-weight: 200; line-height: 27px; } h1, h2, h3, h4, h5, h6 { margin: 0; font-family: inherit; font-weight: bold; color: inherit; text-rendering: optimizelegibility; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { font-weight: normal; color: #999999; } h1 { font-size: 30px; line-height: 36px; } h1 small { font-size: 18px; } h2 { font-size: 24px; line-height: 36px; } h2 small { font-size: 18px; } h3 { font-size: 18px; line-height: 27px; } h3 small { font-size: 14px; } h4, h5, h6 { line-height: 18px; } h4 { font-size: 14px; } h4 small { font-size: 12px; } h5 { font-size: 12px; } h6 { font-size: 11px; color: #999999; text-transform: uppercase; } .page-header { padding-bottom: 17px; margin: 18px 0; border-bottom: 1px solid #eeeeee; } .page-header h1 { line-height: 1; } ul, ol { padding: 0; margin: 0 0 9px 25px; } ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; } ul { list-style: disc; } ol { list-style: decimal; } li { line-height: 18px; } ul.unstyled, ol.unstyled { margin-left: 0; list-style: none; } dl { margin-bottom: 18px; } dt, dd { line-height: 18px; } dt { font-weight: bold; line-height: 17px; } dd { margin-left: 9px; } .dl-horizontal dt { float: left; width: 120px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 130px; } hr { margin: 18px 0; border: 0; border-top: 1px solid #eeeeee; border-bottom: 1px solid #ffffff; } strong { font-weight: bold; } em { font-style: italic; } .muted { color: #999999; } abbr[title] { cursor: help; border-bottom: 1px dotted #999999; } abbr.initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 0 0 0 15px; margin: 0 0 18px; border-left: 5px solid #eeeeee; } blockquote p { margin-bottom: 0; font-size: 16px; font-weight: 300; line-height: 22.5px; } blockquote small { display: block; line-height: 18px; color: #999999; } blockquote small:before { content: '\2014 \00A0'; } blockquote.pull-right { float: right; padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; } blockquote.pull-right p, blockquote.pull-right small { text-align: right; } q:before, q:after, blockquote:before, blockquote:after { content: ""; } address { display: block; margin-bottom: 18px; font-style: normal; line-height: 18px; } small { font-size: 100%; } cite { font-style: normal; } code, pre { padding: 0 3px 2px; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 12px; color: #333333; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } code { padding: 2px 4px; color: #d14; background-color: #f7f7f9; border: 1px solid #e1e1e8; } pre { display: block; padding: 8.5px; margin: 0 0 9px; font-size: 12.025px; line-height: 18px; word-break: break-all; word-wrap: break-word; white-space: pre; white-space: pre-wrap; background-color: #f5f5f5; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.15); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } pre.prettyprint { margin-bottom: 18px; } pre code { padding: 0; color: inherit; background-color: transparent; border: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } form { margin: 0 0 18px; } fieldset { padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 27px; font-size: 19.5px; line-height: 36px; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } legend small { font-size: 13.5px; color: #999999; } label, input, button, select, textarea { font-size: 13px; font-weight: normal; line-height: 18px; } input, button, select, textarea { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } label { display: block; margin-bottom: 5px; } select, textarea, input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"], .uneditable-input { display: inline-block; height: 18px; padding: 4px; margin-bottom: 9px; font-size: 13px; line-height: 18px; color: #555555; } input, textarea { width: 210px; } textarea { height: auto; } textarea, input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"], .uneditable-input { background-color: #ffffff; border: 1px solid #cccccc; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; -moz-transition: border linear 0.2s, box-shadow linear 0.2s; -ms-transition: border linear 0.2s, box-shadow linear 0.2s; -o-transition: border linear 0.2s, box-shadow linear 0.2s; transition: border linear 0.2s, box-shadow linear 0.2s; } textarea:focus, input[type="text"]:focus, input[type="password"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="date"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, input[type="number"]:focus, input[type="email"]:focus, input[type="url"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="color"]:focus, .uneditable-input:focus { border-color: rgba(82, 168, 236, 0.8); outline: 0; outline: thin dotted \9; /* IE6-9 */ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); } input[type="radio"], input[type="checkbox"] { margin: 3px 0; *margin-top: 0; /* IE7 */ line-height: normal; cursor: pointer; } input[type="submit"], input[type="reset"], input[type="button"], input[type="radio"], input[type="checkbox"] { width: auto; } .uneditable-textarea { width: auto; height: auto; } select, input[type="file"] { height: 28px; /* In IE7, the height of the select element cannot be changed by height, only font-size */ *margin-top: 4px; /* For IE7, add top margin to align select with labels */ line-height: 28px; } select { width: 220px; border: 1px solid #bbb; } select[multiple], select[size] { height: auto; } select:focus, input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .radio, .checkbox { min-height: 18px; padding-left: 18px; } .radio input[type="radio"], .checkbox input[type="checkbox"] { float: left; margin-left: -18px; } .controls > .radio:first-child, .controls > .checkbox:first-child { padding-top: 5px; } .radio.inline, .checkbox.inline { display: inline-block; padding-top: 5px; margin-bottom: 0; vertical-align: middle; } .radio.inline + .radio.inline, .checkbox.inline + .checkbox.inline { margin-left: 10px; } .input-mini { width: 60px; } .input-small { width: 90px; } .input-medium { width: 150px; } .input-large { width: 210px; } .input-xlarge { width: 270px; } .input-xxlarge { width: 530px; } input[class*="span"], select[class*="span"], textarea[class*="span"], .uneditable-input[class*="span"], .row-fluid input[class*="span"], .row-fluid select[class*="span"], .row-fluid textarea[class*="span"], .row-fluid .uneditable-input[class*="span"] { float: none; margin-left: 0; } .input-append input[class*="span"], .input-append .uneditable-input[class*="span"], .input-prepend input[class*="span"], .input-prepend .uneditable-input[class*="span"], .row-fluid .input-prepend [class*="span"], .row-fluid .input-append [class*="span"] { display: inline-block; } input, textarea, .uneditable-input { margin-left: 0; } input.span12, textarea.span12, .uneditable-input.span12 { width: 930px; } input.span11, textarea.span11, .uneditable-input.span11 { width: 850px; } input.span10, textarea.span10, .uneditable-input.span10 { width: 770px; } input.span9, textarea.span9, .uneditable-input.span9 { width: 690px; } input.span8, textarea.span8, .uneditable-input.span8 { width: 610px; } input.span7, textarea.span7, .uneditable-input.span7 { width: 530px; } input.span6, textarea.span6, .uneditable-input.span6 { width: 450px; } input.span5, textarea.span5, .uneditable-input.span5 { width: 370px; } input.span4, textarea.span4, .uneditable-input.span4 { width: 290px; } input.span3, textarea.span3, .uneditable-input.span3 { width: 210px; } input.span2, textarea.span2, .uneditable-input.span2 { width: 130px; } input.span1, textarea.span1, .uneditable-input.span1 { width: 50px; } input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] { cursor: not-allowed; background-color: #eeeeee; border-color: #ddd; } input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"][readonly], input[type="checkbox"][readonly] { background-color: transparent; } .control-group.warning > label, .control-group.warning .help-block, .control-group.warning .help-inline { color: #c09853; } .control-group.warning .checkbox, .control-group.warning .radio, .control-group.warning input, .control-group.warning select, .control-group.warning textarea { color: #c09853; border-color: #c09853; } .control-group.warning .checkbox:focus, .control-group.warning .radio:focus, .control-group.warning input:focus, .control-group.warning select:focus, .control-group.warning textarea:focus { border-color: #a47e3c; -webkit-box-shadow: 0 0 6px #dbc59e; -moz-box-shadow: 0 0 6px #dbc59e; box-shadow: 0 0 6px #dbc59e; } .control-group.warning .input-prepend .add-on, .control-group.warning .input-append .add-on { color: #c09853; background-color: #fcf8e3; border-color: #c09853; } .control-group.error > label, .control-group.error .help-block, .control-group.error .help-inline { color: #b94a48; } .control-group.error .checkbox, .control-group.error .radio, .control-group.error input, .control-group.error select, .control-group.error textarea { color: #b94a48; border-color: #b94a48; } .control-group.error .checkbox:focus, .control-group.error .radio:focus, .control-group.error input:focus, .control-group.error select:focus, .control-group.error textarea:focus { border-color: #953b39; -webkit-box-shadow: 0 0 6px #d59392; -moz-box-shadow: 0 0 6px #d59392; box-shadow: 0 0 6px #d59392; } .control-group.error .input-prepend .add-on, .control-group.error .input-append .add-on { color: #b94a48; background-color: #f2dede; border-color: #b94a48; } .control-group.success > label, .control-group.success .help-block, .control-group.success .help-inline { color: #468847; } .control-group.success .checkbox, .control-group.success .radio, .control-group.success input, .control-group.success select, .control-group.success textarea { color: #468847; border-color: #468847; } .control-group.success .checkbox:focus, .control-group.success .radio:focus, .control-group.success input:focus, .control-group.success select:focus, .control-group.success textarea:focus { border-color: #356635; -webkit-box-shadow: 0 0 6px #7aba7b; -moz-box-shadow: 0 0 6px #7aba7b; box-shadow: 0 0 6px #7aba7b; } .control-group.success .input-prepend .add-on, .control-group.success .input-append .add-on { color: #468847; background-color: #dff0d8; border-color: #468847; } input:focus:required:invalid, textarea:focus:required:invalid, select:focus:required:invalid { color: #b94a48; border-color: #ee5f5b; } input:focus:required:invalid:focus, textarea:focus:required:invalid:focus, select:focus:required:invalid:focus { border-color: #e9322d; -webkit-box-shadow: 0 0 6px #f8b9b7; -moz-box-shadow: 0 0 6px #f8b9b7; box-shadow: 0 0 6px #f8b9b7; } .form-actions { padding: 17px 20px 18px; margin-top: 18px; margin-bottom: 18px; background-color: #f5f5f5; border-top: 1px solid #e5e5e5; *zoom: 1; } .form-actions:before, .form-actions:after { display: table; content: ""; } .form-actions:after { clear: both; } .uneditable-input { overflow: hidden; white-space: nowrap; cursor: not-allowed; background-color: #ffffff; border-color: #eee; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); } :-moz-placeholder { color: #999999; } :-ms-input-placeholder { color: #999999; } ::-webkit-input-placeholder { color: #999999; } .help-block, .help-inline { color: #555555; } .help-block { display: block; margin-bottom: 9px; } .help-inline { display: inline-block; *display: inline; padding-left: 5px; vertical-align: middle; *zoom: 1; } .input-prepend, .input-append { margin-bottom: 5px; } .input-prepend input, .input-append input, .input-prepend select, .input-append select, .input-prepend .uneditable-input, .input-append .uneditable-input { position: relative; margin-bottom: 0; *margin-left: 0; vertical-align: middle; -webkit-border-radius: 0 3px 3px 0; -moz-border-radius: 0 3px 3px 0; border-radius: 0 3px 3px 0; } .input-prepend input:focus, .input-append input:focus, .input-prepend select:focus, .input-append select:focus, .input-prepend .uneditable-input:focus, .input-append .uneditable-input:focus { z-index: 2; } .input-prepend .uneditable-input, .input-append .uneditable-input { border-left-color: #ccc; } .input-prepend .add-on, .input-append .add-on { display: inline-block; width: auto; height: 18px; min-width: 16px; padding: 4px 5px; font-weight: normal; line-height: 18px; text-align: center; text-shadow: 0 1px 0 #ffffff; vertical-align: middle; background-color: #eeeeee; border: 1px solid #ccc; } .input-prepend .add-on, .input-append .add-on, .input-prepend .btn, .input-append .btn { margin-left: -1px; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .input-prepend .active, .input-append .active { background-color: #a9dba9; border-color: #46a546; } .input-prepend .add-on, .input-prepend .btn { margin-right: -1px; } .input-prepend .add-on:first-child, .input-prepend .btn:first-child { -webkit-border-radius: 3px 0 0 3px; -moz-border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px; } .input-append input, .input-append select, .input-append .uneditable-input { -webkit-border-radius: 3px 0 0 3px; -moz-border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px; } .input-append .uneditable-input { border-right-color: #ccc; border-left-color: #eee; } .input-append .add-on:last-child, .input-append .btn:last-child { -webkit-border-radius: 0 3px 3px 0; -moz-border-radius: 0 3px 3px 0; border-radius: 0 3px 3px 0; } .input-prepend.input-append input, .input-prepend.input-append select, .input-prepend.input-append .uneditable-input { -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .input-prepend.input-append .add-on:first-child, .input-prepend.input-append .btn:first-child { margin-right: -1px; -webkit-border-radius: 3px 0 0 3px; -moz-border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px; } .input-prepend.input-append .add-on:last-child, .input-prepend.input-append .btn:last-child { margin-left: -1px; -webkit-border-radius: 0 3px 3px 0; -moz-border-radius: 0 3px 3px 0; border-radius: 0 3px 3px 0; } .search-query { padding-right: 14px; padding-right: 4px \9; padding-left: 14px; padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */ margin-bottom: 0; -webkit-border-radius: 14px; -moz-border-radius: 14px; border-radius: 14px; } .form-search input, .form-inline input, .form-horizontal input, .form-search textarea, .form-inline textarea, .form-horizontal textarea, .form-search select, .form-inline select, .form-horizontal select, .form-search .help-inline, .form-inline .help-inline, .form-horizontal .help-inline, .form-search .uneditable-input, .form-inline .uneditable-input, .form-horizontal .uneditable-input, .form-search .input-prepend, .form-inline .input-prepend, .form-horizontal .input-prepend, .form-search .input-append, .form-inline .input-append, .form-horizontal .input-append { display: inline-block; *display: inline; margin-bottom: 0; *zoom: 1; } .form-search .hide, .form-inline .hide, .form-horizontal .hide { display: none; } .form-search label, .form-inline label { display: inline-block; } .form-search .input-append, .form-inline .input-append, .form-search .input-prepend, .form-inline .input-prepend { margin-bottom: 0; } .form-search .radio, .form-search .checkbox, .form-inline .radio, .form-inline .checkbox { padding-left: 0; margin-bottom: 0; vertical-align: middle; } .form-search .radio input[type="radio"], .form-search .checkbox input[type="checkbox"], .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { float: left; margin-right: 3px; margin-left: 0; } .control-group { margin-bottom: 9px; } legend + .control-group { margin-top: 18px; -webkit-margin-top-collapse: separate; } .form-horizontal .control-group { margin-bottom: 18px; *zoom: 1; } .form-horizontal .control-group:before, .form-horizontal .control-group:after { display: table; content: ""; } .form-horizontal .control-group:after { clear: both; } .form-horizontal .control-label { float: left; width: 140px; padding-top: 5px; text-align: right; } .form-horizontal .controls { *display: inline-block; *padding-left: 20px; margin-left: 160px; *margin-left: 0; } .form-horizontal .controls:first-child { *padding-left: 160px; } .form-horizontal .help-block { margin-top: 9px; margin-bottom: 0; } .form-horizontal .form-actions { padding-left: 160px; } table { max-width: 100%; background-color: transparent; border-collapse: collapse; border-spacing: 0; } .table { width: 100%; margin-bottom: 18px; } .table th, .table td { padding: 8px; line-height: 18px; text-align: left; vertical-align: top; border-top: 1px solid #dddddd; } .table th { font-weight: bold; } .table thead th { vertical-align: bottom; } .table caption + thead tr:first-child th, .table caption + thead tr:first-child td, .table colgroup + thead tr:first-child th, .table colgroup + thead tr:first-child td, .table thead:first-child tr:first-child th, .table thead:first-child tr:first-child td { border-top: 0; } .table tbody + tbody { border-top: 2px solid #dddddd; } .table-condensed th, .table-condensed td { padding: 4px 5px; } .table-bordered { border: 1px solid #dddddd; border-collapse: separate; *border-collapse: collapsed; border-left: 0; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .table-bordered th, .table-bordered td { border-left: 1px solid #dddddd; } .table-bordered caption + thead tr:first-child th, .table-bordered caption + tbody tr:first-child th, .table-bordered caption + tbody tr:first-child td, .table-bordered colgroup + thead tr:first-child th, .table-bordered colgroup + tbody tr:first-child th, .table-bordered colgroup + tbody tr:first-child td, .table-bordered thead:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child td { border-top: 0; } .table-bordered thead:first-child tr:first-child th:first-child, .table-bordered tbody:first-child tr:first-child td:first-child { -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topleft: 4px; } .table-bordered thead:first-child tr:first-child th:last-child, .table-bordered tbody:first-child tr:first-child td:last-child { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-topright: 4px; } .table-bordered thead:last-child tr:last-child th:first-child, .table-bordered tbody:last-child tr:last-child td:first-child { -webkit-border-radius: 0 0 0 4px; -moz-border-radius: 0 0 0 4px; border-radius: 0 0 0 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomleft: 4px; } .table-bordered thead:last-child tr:last-child th:last-child, .table-bordered tbody:last-child tr:last-child td:last-child { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; -moz-border-radius-bottomright: 4px; } .table-striped tbody tr:nth-child(odd) td, .table-striped tbody tr:nth-child(odd) th { background-color: #f9f9f9; } .table tbody tr:hover td, .table tbody tr:hover th { background-color: #f5f5f5; } table .span1 { float: none; width: 44px; margin-left: 0; } table .span2 { float: none; width: 124px; margin-left: 0; } table .span3 { float: none; width: 204px; margin-left: 0; } table .span4 { float: none; width: 284px; margin-left: 0; } table .span5 { float: none; width: 364px; margin-left: 0; } table .span6 { float: none; width: 444px; margin-left: 0; } table .span7 { float: none; width: 524px; margin-left: 0; } table .span8 { float: none; width: 604px; margin-left: 0; } table .span9 { float: none; width: 684px; margin-left: 0; } table .span10 { float: none; width: 764px; margin-left: 0; } table .span11 { float: none; width: 844px; margin-left: 0; } table .span12 { float: none; width: 924px; margin-left: 0; } table .span13 { float: none; width: 1004px; margin-left: 0; } table .span14 { float: none; width: 1084px; margin-left: 0; } table .span15 { float: none; width: 1164px; margin-left: 0; } table .span16 { float: none; width: 1244px; margin-left: 0; } table .span17 { float: none; width: 1324px; margin-left: 0; } table .span18 { float: none; width: 1404px; margin-left: 0; } table .span19 { float: none; width: 1484px; margin-left: 0; } table .span20 { float: none; width: 1564px; margin-left: 0; } table .span21 { float: none; width: 1644px; margin-left: 0; } table .span22 { float: none; width: 1724px; margin-left: 0; } table .span23 { float: none; width: 1804px; margin-left: 0; } table .span24 { float: none; width: 1884px; margin-left: 0; } [class^="icon-"], [class*=" icon-"] { display: inline-block; width: 14px; height: 14px; *margin-right: .3em; line-height: 14px; vertical-align: text-top; background-image: url("../img/glyphicons-halflings.png"); background-position: 14px 14px; background-repeat: no-repeat; } [class^="icon-"]:last-child, [class*=" icon-"]:last-child { *margin-left: 0; } .icon-white { background-image: url("../img/glyphicons-halflings-white.png"); } .icon-glass { background-position: 0 0; } .icon-music { background-position: -24px 0; } .icon-search { background-position: -48px 0; } .icon-envelope { background-position: -72px 0; } .icon-heart { background-position: -96px 0; } .icon-star { background-position: -120px 0; } .icon-star-empty { background-position: -144px 0; } .icon-user { background-position: -168px 0; } .icon-film { background-position: -192px 0; } .icon-th-large { background-position: -216px 0; } .icon-th { background-position: -240px 0; } .icon-th-list { background-position: -264px 0; } .icon-ok { background-position: -288px 0; } .icon-remove { background-position: -312px 0; } .icon-zoom-in { background-position: -336px 0; } .icon-zoom-out { background-position: -360px 0; } .icon-off { background-position: -384px 0; } .icon-signal { background-position: -408px 0; } .icon-cog { background-position: -432px 0; } .icon-trash { background-position: -456px 0; } .icon-home { background-position: 0 -24px; } .icon-file { background-position: -24px -24px; } .icon-time { background-position: -48px -24px; } .icon-road { background-position: -72px -24px; } .icon-download-alt { background-position: -96px -24px; } .icon-download { background-position: -120px -24px; } .icon-upload { background-position: -144px -24px; } .icon-inbox { background-position: -168px -24px; } .icon-play-circle { background-position: -192px -24px; } .icon-repeat { background-position: -216px -24px; } .icon-refresh { background-position: -240px -24px; } .icon-list-alt { background-position: -264px -24px; } .icon-lock { background-position: -287px -24px; } .icon-flag { background-position: -312px -24px; } .icon-headphones { background-position: -336px -24px; } .icon-volume-off { background-position: -360px -24px; } .icon-volume-down { background-position: -384px -24px; } .icon-volume-up { background-position: -408px -24px; } .icon-qrcode { background-position: -432px -24px; } .icon-barcode { background-position: -456px -24px; } .icon-tag { background-position: 0 -48px; } .icon-tags { background-position: -25px -48px; } .icon-book { background-position: -48px -48px; } .icon-bookmark { background-position: -72px -48px; } .icon-print { background-position: -96px -48px; } .icon-camera { background-position: -120px -48px; } .icon-font { background-position: -144px -48px; } .icon-bold { background-position: -167px -48px; } .icon-italic { background-position: -192px -48px; } .icon-text-height { background-position: -216px -48px; } .icon-text-width { background-position: -240px -48px; } .icon-align-left { background-position: -264px -48px; } .icon-align-center { background-position: -288px -48px; } .icon-align-right { background-position: -312px -48px; } .icon-align-justify { background-position: -336px -48px; } .icon-list { background-position: -360px -48px; } .icon-indent-left { background-position: -384px -48px; } .icon-indent-right { background-position: -408px -48px; } .icon-facetime-video { background-position: -432px -48px; } .icon-picture { background-position: -456px -48px; } .icon-pencil { background-position: 0 -72px; } .icon-map-marker { background-position: -24px -72px; } .icon-adjust { background-position: -48px -72px; } .icon-tint { background-position: -72px -72px; } .icon-edit { background-position: -96px -72px; } .icon-share { background-position: -120px -72px; } .icon-check { background-position: -144px -72px; } .icon-move { background-position: -168px -72px; } .icon-step-backward { background-position: -192px -72px; } .icon-fast-backward { background-position: -216px -72px; } .icon-backward { background-position: -240px -72px; } .icon-play { background-position: -264px -72px; } .icon-pause { background-position: -288px -72px; } .icon-stop { background-position: -312px -72px; } .icon-forward { background-position: -336px -72px; } .icon-fast-forward { background-position: -360px -72px; } .icon-step-forward { background-position: -384px -72px; } .icon-eject { background-position: -408px -72px; } .icon-chevron-left { background-position: -432px -72px; } .icon-chevron-right { background-position: -456px -72px; } .icon-plus-sign { background-position: 0 -96px; } .icon-minus-sign { background-position: -24px -96px; } .icon-remove-sign { background-position: -48px -96px; } .icon-ok-sign { background-position: -72px -96px; } .icon-question-sign { background-position: -96px -96px; } .icon-info-sign { background-position: -120px -96px; } .icon-screenshot { background-position: -144px -96px; } .icon-remove-circle { background-position: -168px -96px; } .icon-ok-circle { background-position: -192px -96px; } .icon-ban-circle { background-position: -216px -96px; } .icon-arrow-left { background-position: -240px -96px; } .icon-arrow-right { background-position: -264px -96px; } .icon-arrow-up { background-position: -289px -96px; } .icon-arrow-down { background-position: -312px -96px; } .icon-share-alt { background-position: -336px -96px; } .icon-resize-full { background-position: -360px -96px; } .icon-resize-small { background-position: -384px -96px; } .icon-plus { background-position: -408px -96px; } .icon-minus { background-position: -433px -96px; } .icon-asterisk { background-position: -456px -96px; } .icon-exclamation-sign { background-position: 0 -120px; } .icon-gift { background-position: -24px -120px; } .icon-leaf { background-position: -48px -120px; } .icon-fire { background-position: -72px -120px; } .icon-eye-open { background-position: -96px -120px; } .icon-eye-close { background-position: -120px -120px; } .icon-warning-sign { background-position: -144px -120px; } .icon-plane { background-position: -168px -120px; } .icon-calendar { background-position: -192px -120px; } .icon-random { background-position: -216px -120px; } .icon-comment { background-position: -240px -120px; } .icon-magnet { background-position: -264px -120px; } .icon-chevron-up { background-position: -288px -120px; } .icon-chevron-down { background-position: -313px -119px; } .icon-retweet { background-position: -336px -120px; } .icon-shopping-cart { background-position: -360px -120px; } .icon-folder-close { background-position: -384px -120px; } .icon-folder-open { background-position: -408px -120px; } .icon-resize-vertical { background-position: -432px -119px; } .icon-resize-horizontal { background-position: -456px -118px; } .icon-hdd { background-position: 0 -144px; } .icon-bullhorn { background-position: -24px -144px; } .icon-bell { background-position: -48px -144px; } .icon-certificate { background-position: -72px -144px; } .icon-thumbs-up { background-position: -96px -144px; } .icon-thumbs-down { background-position: -120px -144px; } .icon-hand-right { background-position: -144px -144px; } .icon-hand-left { background-position: -168px -144px; } .icon-hand-up { background-position: -192px -144px; } .icon-hand-down { background-position: -216px -144px; } .icon-circle-arrow-right { background-position: -240px -144px; } .icon-circle-arrow-left { background-position: -264px -144px; } .icon-circle-arrow-up { background-position: -288px -144px; } .icon-circle-arrow-down { background-position: -312px -144px; } .icon-globe { background-position: -336px -144px; } .icon-wrench { background-position: -360px -144px; } .icon-tasks { background-position: -384px -144px; } .icon-filter { background-position: -408px -144px; } .icon-briefcase { background-position: -432px -144px; } .icon-fullscreen { background-position: -456px -144px; } .dropup, .dropdown { position: relative; } .dropdown-toggle { *margin-bottom: -3px; } .dropdown-toggle:active, .open .dropdown-toggle { outline: 0; } .caret { display: inline-block; width: 0; height: 0; vertical-align: top; border-top: 4px solid #000000; border-right: 4px solid transparent; border-left: 4px solid transparent; content: ""; opacity: 0.3; filter: alpha(opacity=30); } .dropdown .caret { margin-top: 8px; margin-left: 2px; } .dropdown:hover .caret, .open .caret { opacity: 1; filter: alpha(opacity=100); } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 4px 0; margin: 1px 0 0; list-style: none; background-color: #ffffff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); *border-right-width: 2px; *border-bottom-width: 2px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { *width: 100%; height: 1px; margin: 8px 1px; *margin: -5px 0 5px; overflow: hidden; background-color: #e5e5e5; border-bottom: 1px solid #ffffff; } .dropdown-menu a { display: block; padding: 3px 15px; clear: both; font-weight: normal; line-height: 18px; color: #333333; white-space: nowrap; } .dropdown-menu li > a:hover, .dropdown-menu .active > a, .dropdown-menu .active > a:hover { color: #ffffff; text-decoration: none; background-color: #0088cc; } .open { *z-index: 1000; } .open > .dropdown-menu { display: block; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { border-top: 0; border-bottom: 4px solid #000000; content: "\2191"; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 1px; } .typeahead { margin-top: 2px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #eee; border: 1px solid rgba(0, 0, 0, 0.05); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, 0.15); } .well-large { padding: 24px; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .well-small { padding: 9px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; -moz-transition: opacity 0.15s linear; -ms-transition: opacity 0.15s linear; -o-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .collapse { position: relative; height: 0; overflow: hidden; -webkit-transition: height 0.35s ease; -moz-transition: height 0.35s ease; -ms-transition: height 0.35s ease; -o-transition: height 0.35s ease; transition: height 0.35s ease; } .collapse.in { height: auto; } .close { float: right; font-size: 20px; font-weight: bold; line-height: 18px; color: #000000; text-shadow: 0 1px 0 #ffffff; opacity: 0.2; filter: alpha(opacity=20); } .close:hover { color: #000000; text-decoration: none; cursor: pointer; opacity: 0.4; filter: alpha(opacity=40); } button.close { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } .btn { display: inline-block; *display: inline; padding: 4px 10px 4px; margin-bottom: 0; *margin-left: .3em; font-size: 13px; line-height: 18px; *line-height: 20px; color: #333333; text-align: center; text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); vertical-align: middle; cursor: pointer; background-color: #f5f5f5; *background-color: #e6e6e6; background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); background-image: linear-gradient(top, #ffffff, #e6e6e6); background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); background-repeat: repeat-x; border: 1px solid #cccccc; *border: 0; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border-color: #e6e6e6 #e6e6e6 #bfbfbf; border-bottom-color: #b3b3b3; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); *zoom: 1; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn:hover, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { background-color: #e6e6e6; *background-color: #d9d9d9; } .btn:active, .btn.active { background-color: #cccccc \9; } .btn:first-child { *margin-left: 0; } .btn:hover { color: #333333; text-decoration: none; background-color: #e6e6e6; *background-color: #d9d9d9; /* Buttons in IE7 don't get borders, so darken on hover */ background-position: 0 -15px; -webkit-transition: background-position 0.1s linear; -moz-transition: background-position 0.1s linear; -ms-transition: background-position 0.1s linear; -o-transition: background-position 0.1s linear; transition: background-position 0.1s linear; } .btn:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn.active, .btn:active { background-color: #e6e6e6; background-color: #d9d9d9 \9; background-image: none; outline: 0; -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn.disabled, .btn[disabled] { cursor: default; background-color: #e6e6e6; background-image: none; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } .btn-large { padding: 9px 14px; font-size: 15px; line-height: normal; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } .btn-large [class^="icon-"] { margin-top: 1px; } .btn-small { padding: 5px 9px; font-size: 11px; line-height: 16px; } .btn-small [class^="icon-"] { margin-top: -1px; } .btn-mini { padding: 2px 6px; font-size: 11px; line-height: 14px; } .btn-primary, .btn-primary:hover, .btn-warning, .btn-warning:hover, .btn-danger, .btn-danger:hover, .btn-success, .btn-success:hover, .btn-info, .btn-info:hover, .btn-inverse, .btn-inverse:hover { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .btn-primary.active, .btn-warning.active, .btn-danger.active, .btn-success.active, .btn-info.active, .btn-inverse.active { color: rgba(255, 255, 255, 0.75); } .btn { border-color: #ccc; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); } .btn-primary { background-color: #0074cc; *background-color: #0055cc; background-image: -ms-linear-gradient(top, #0088cc, #0055cc); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc)); background-image: -webkit-linear-gradient(top, #0088cc, #0055cc); background-image: -o-linear-gradient(top, #0088cc, #0055cc); background-image: -moz-linear-gradient(top, #0088cc, #0055cc); background-image: linear-gradient(top, #0088cc, #0055cc); background-repeat: repeat-x; border-color: #0055cc #0055cc #003580; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); } .btn-primary:hover, .btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] { background-color: #0055cc; *background-color: #004ab3; } .btn-primary:active, .btn-primary.active { background-color: #004099 \9; } .btn-warning { background-color: #faa732; *background-color: #f89406; background-image: -ms-linear-gradient(top, #fbb450, #f89406); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); background-image: -webkit-linear-gradient(top, #fbb450, #f89406); background-image: -o-linear-gradient(top, #fbb450, #f89406); background-image: -moz-linear-gradient(top, #fbb450, #f89406); background-image: linear-gradient(top, #fbb450, #f89406); background-repeat: repeat-x; border-color: #f89406 #f89406 #ad6704; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); } .btn-warning:hover, .btn-warning:active, .btn-warning.active, .btn-warning.disabled, .btn-warning[disabled] { background-color: #f89406; *background-color: #df8505; } .btn-warning:active, .btn-warning.active { background-color: #c67605 \9; } .btn-danger { background-color: #da4f49; *background-color: #bd362f; background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); background-image: linear-gradient(top, #ee5f5b, #bd362f); background-repeat: repeat-x; border-color: #bd362f #bd362f #802420; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); } .btn-danger:hover, .btn-danger:active, .btn-danger.active, .btn-danger.disabled, .btn-danger[disabled] { background-color: #bd362f; *background-color: #a9302a; } .btn-danger:active, .btn-danger.active { background-color: #942a25 \9; } .btn-success { background-color: #5bb75b; *background-color: #51a351; background-image: -ms-linear-gradient(top, #62c462, #51a351); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); background-image: -webkit-linear-gradient(top, #62c462, #51a351); background-image: -o-linear-gradient(top, #62c462, #51a351); background-image: -moz-linear-gradient(top, #62c462, #51a351); background-image: linear-gradient(top, #62c462, #51a351); background-repeat: repeat-x; border-color: #51a351 #51a351 #387038; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); } .btn-success:hover, .btn-success:active, .btn-success.active, .btn-success.disabled, .btn-success[disabled] { background-color: #51a351; *background-color: #499249; } .btn-success:active, .btn-success.active { background-color: #408140 \9; } .btn-info { background-color: #49afcd; *background-color: #2f96b4; background-image: -ms-linear-gradient(top, #5bc0de, #2f96b4); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); background-image: linear-gradient(top, #5bc0de, #2f96b4); background-repeat: repeat-x; border-color: #2f96b4 #2f96b4 #1f6377; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); } .btn-info:hover, .btn-info:active, .btn-info.active, .btn-info.disabled, .btn-info[disabled] { background-color: #2f96b4; *background-color: #2a85a0; } .btn-info:active, .btn-info.active { background-color: #24748c \9; } .btn-inverse { background-color: #414141; *background-color: #222222; background-image: -ms-linear-gradient(top, #555555, #222222); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222)); background-image: -webkit-linear-gradient(top, #555555, #222222); background-image: -o-linear-gradient(top, #555555, #222222); background-image: -moz-linear-gradient(top, #555555, #222222); background-image: linear-gradient(top, #555555, #222222); background-repeat: repeat-x; border-color: #222222 #222222 #000000; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); } .btn-inverse:hover, .btn-inverse:active, .btn-inverse.active, .btn-inverse.disabled, .btn-inverse[disabled] { background-color: #222222; *background-color: #151515; } .btn-inverse:active, .btn-inverse.active { background-color: #080808 \9; } button.btn, input[type="submit"].btn { *padding-top: 2px; *padding-bottom: 2px; } button.btn::-moz-focus-inner, input[type="submit"].btn::-moz-focus-inner { padding: 0; border: 0; } button.btn.btn-large, input[type="submit"].btn.btn-large { *padding-top: 7px; *padding-bottom: 7px; } button.btn.btn-small, input[type="submit"].btn.btn-small { *padding-top: 3px; *padding-bottom: 3px; } button.btn.btn-mini, input[type="submit"].btn.btn-mini { *padding-top: 1px; *padding-bottom: 1px; } .btn-group { position: relative; *margin-left: .3em; *zoom: 1; } .btn-group:before, .btn-group:after { display: table; content: ""; } .btn-group:after { clear: both; } .btn-group:first-child { *margin-left: 0; } .btn-group + .btn-group { margin-left: 5px; } .btn-toolbar { margin-top: 9px; margin-bottom: 9px; } .btn-toolbar .btn-group { display: inline-block; *display: inline; /* IE7 inline-block hack */ *zoom: 1; } .btn-group > .btn { position: relative; float: left; margin-left: -1px; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -moz-border-radius-topleft: 4px; } .btn-group > .btn:last-child, .btn-group > .dropdown-toggle { -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; -moz-border-radius-topright: 4px; -moz-border-radius-bottomright: 4px; } .btn-group > .btn.large:first-child { margin-left: 0; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-bottomleft: 6px; -moz-border-radius-topleft: 6px; } .btn-group > .btn.large:last-child, .btn-group > .large.dropdown-toggle { -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; -moz-border-radius-topright: 6px; -moz-border-radius-bottomright: 6px; } .btn-group > .btn:hover, .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active { z-index: 2; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group > .dropdown-toggle { *padding-top: 4px; padding-right: 8px; *padding-bottom: 4px; padding-left: 8px; -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn-group > .btn-mini.dropdown-toggle { padding-right: 5px; padding-left: 5px; } .btn-group > .btn-small.dropdown-toggle { *padding-top: 4px; *padding-bottom: 4px; } .btn-group > .btn-large.dropdown-toggle { padding-right: 12px; padding-left: 12px; } .btn-group.open .dropdown-toggle { background-image: none; -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); } .btn-group.open .btn.dropdown-toggle { background-color: #e6e6e6; } .btn-group.open .btn-primary.dropdown-toggle { background-color: #0055cc; } .btn-group.open .btn-warning.dropdown-toggle { background-color: #f89406; } .btn-group.open .btn-danger.dropdown-toggle { background-color: #bd362f; } .btn-group.open .btn-success.dropdown-toggle { background-color: #51a351; } .btn-group.open .btn-info.dropdown-toggle { background-color: #2f96b4; } .btn-group.open .btn-inverse.dropdown-toggle { background-color: #222222; } .btn .caret { margin-top: 7px; margin-left: 0; } .btn:hover .caret, .open.btn-group .caret { opacity: 1; filter: alpha(opacity=100); } .btn-mini .caret { margin-top: 5px; } .btn-small .caret { margin-top: 6px; } .btn-large .caret { margin-top: 6px; border-top-width: 5px; border-right-width: 5px; border-left-width: 5px; } .dropup .btn-large .caret { border-top: 0; border-bottom: 5px solid #000000; } .btn-primary .caret, .btn-warning .caret, .btn-danger .caret, .btn-info .caret, .btn-success .caret, .btn-inverse .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; opacity: 0.75; filter: alpha(opacity=75); } .alert { padding: 8px 35px 8px 14px; margin-bottom: 18px; color: #c09853; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); background-color: #fcf8e3; border: 1px solid #fbeed5; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .alert-heading { color: inherit; } .alert .close { position: relative; top: -2px; right: -21px; line-height: 18px; } .alert-success { color: #468847; background-color: #dff0d8; border-color: #d6e9c6; } .alert-danger, .alert-error { color: #b94a48; background-color: #f2dede; border-color: #eed3d7; } .alert-info { color: #3a87ad; background-color: #d9edf7; border-color: #bce8f1; } .alert-block { padding-top: 14px; padding-bottom: 14px; } .alert-block > p, .alert-block > ul { margin-bottom: 0; } .alert-block p + p { margin-top: 5px; } .nav { margin-bottom: 18px; margin-left: 0; list-style: none; } .nav > li > a { display: block; } .nav > li > a:hover { text-decoration: none; background-color: #eeeeee; } .nav > .pull-right { float: right; } .nav .nav-header { display: block; padding: 3px 15px; font-size: 11px; font-weight: bold; line-height: 18px; color: #999999; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); text-transform: uppercase; } .nav li + .nav-header { margin-top: 9px; } .nav-list { padding-right: 15px; padding-left: 15px; margin-bottom: 0; } .nav-list > li > a, .nav-list .nav-header { margin-right: -15px; margin-left: -15px; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); } .nav-list > li > a { padding: 3px 15px; } .nav-list > .active > a, .nav-list > .active > a:hover { color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); background-color: #0088cc; } .nav-list [class^="icon-"] { margin-right: 2px; } .nav-list .divider { *width: 100%; height: 1px; margin: 8px 1px; *margin: -5px 0 5px; overflow: hidden; background-color: #e5e5e5; border-bottom: 1px solid #ffffff; } .nav-tabs, .nav-pills { *zoom: 1; } .nav-tabs:before, .nav-pills:before, .nav-tabs:after, .nav-pills:after { display: table; content: ""; } .nav-tabs:after, .nav-pills:after { clear: both; } .nav-tabs > li, .nav-pills > li { float: left; } .nav-tabs > li > a, .nav-pills > li > a { padding-right: 12px; padding-left: 12px; margin-right: 2px; line-height: 14px; } .nav-tabs { border-bottom: 1px solid #ddd; } .nav-tabs > li { margin-bottom: -1px; } .nav-tabs > li > a { padding-top: 8px; padding-bottom: 8px; line-height: 18px; border: 1px solid transparent; -webkit-border-radius: 4px 4px 0 0; -moz-border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #dddddd; } .nav-tabs > .active > a, .nav-tabs > .active > a:hover { color: #555555; cursor: default; background-color: #ffffff; border: 1px solid #ddd; border-bottom-color: transparent; } .nav-pills > li > a { padding-top: 8px; padding-bottom: 8px; margin-top: 2px; margin-bottom: 2px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } .nav-pills > .active > a, .nav-pills > .active > a:hover { color: #ffffff; background-color: #0088cc; } .nav-stacked > li { float: none; } .nav-stacked > li > a { margin-right: 0; } .nav-tabs.nav-stacked { border-bottom: 0; } .nav-tabs.nav-stacked > li > a { border: 1px solid #ddd; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .nav-tabs.nav-stacked > li:first-child > a { -webkit-border-radius: 4px 4px 0 0; -moz-border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0; } .nav-tabs.nav-stacked > li:last-child > a { -webkit-border-radius: 0 0 4px 4px; -moz-border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px; } .nav-tabs.nav-stacked > li > a:hover { z-index: 2; border-color: #ddd; } .nav-pills.nav-stacked > li > a { margin-bottom: 3px; } .nav-pills.nav-stacked > li:last-child > a { margin-bottom: 1px; } .nav-tabs .dropdown-menu { -webkit-border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; border-radius: 0 0 5px 5px; } .nav-pills .dropdown-menu { -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .nav-tabs .dropdown-toggle .caret, .nav-pills .dropdown-toggle .caret { margin-top: 6px; border-top-color: #0088cc; border-bottom-color: #0088cc; } .nav-tabs .dropdown-toggle:hover .caret, .nav-pills .dropdown-toggle:hover .caret { border-top-color: #005580; border-bottom-color: #005580; } .nav-tabs .active .dropdown-toggle .caret, .nav-pills .active .dropdown-toggle .caret { border-top-color: #333333; border-bottom-color: #333333; } .nav > .dropdown.active > a:hover { color: #000000; cursor: pointer; } .nav-tabs .open .dropdown-toggle, .nav-pills .open .dropdown-toggle, .nav > li.dropdown.open.active > a:hover { color: #ffffff; background-color: #999999; border-color: #999999; } .nav li.dropdown.open .caret, .nav li.dropdown.open.active .caret, .nav li.dropdown.open a:hover .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; opacity: 1; filter: alpha(opacity=100); } .tabs-stacked .open > a:hover { border-color: #999999; } .tabbable { *zoom: 1; } .tabbable:before, .tabbable:after { display: table; content: ""; } .tabbable:after { clear: both; } .tab-content { overflow: auto; } .tabs-below > .nav-tabs, .tabs-right > .nav-tabs, .tabs-left > .nav-tabs { border-bottom: 0; } .tab-content > .tab-pane, .pill-content > .pill-pane { display: none; } .tab-content > .active, .pill-content > .active { display: block; } .tabs-below > .nav-tabs { border-top: 1px solid #ddd; } .tabs-below > .nav-tabs > li { margin-top: -1px; margin-bottom: 0; } .tabs-below > .nav-tabs > li > a { -webkit-border-radius: 0 0 4px 4px; -moz-border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px; } .tabs-below > .nav-tabs > li > a:hover { border-top-color: #ddd; border-bottom-color: transparent; } .tabs-below > .nav-tabs > .active > a, .tabs-below > .nav-tabs > .active > a:hover { border-color: transparent #ddd #ddd #ddd; } .tabs-left > .nav-tabs > li, .tabs-right > .nav-tabs > li { float: none; } .tabs-left > .nav-tabs > li > a, .tabs-right > .nav-tabs > li > a { min-width: 74px; margin-right: 0; margin-bottom: 3px; } .tabs-left > .nav-tabs { float: left; margin-right: 19px; border-right: 1px solid #ddd; } .tabs-left > .nav-tabs > li > a { margin-right: -1px; -webkit-border-radius: 4px 0 0 4px; -moz-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; } .tabs-left > .nav-tabs > li > a:hover { border-color: #eeeeee #dddddd #eeeeee #eeeeee; } .tabs-left > .nav-tabs .active > a, .tabs-left > .nav-tabs .active > a:hover { border-color: #ddd transparent #ddd #ddd; *border-right-color: #ffffff; } .tabs-right > .nav-tabs { float: right; margin-left: 19px; border-left: 1px solid #ddd; } .tabs-right > .nav-tabs > li > a { margin-left: -1px; -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } .tabs-right > .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #eeeeee #dddddd; } .tabs-right > .nav-tabs .active > a, .tabs-right > .nav-tabs .active > a:hover { border-color: #ddd #ddd #ddd transparent; *border-left-color: #ffffff; } .navbar { *position: relative; *z-index: 2; margin-bottom: 18px; overflow: visible; } .navbar-inner { min-height: 40px; padding-right: 20px; padding-left: 20px; background-color: #2c2c2c; background-image: -moz-linear-gradient(top, #333333, #222222); background-image: -ms-linear-gradient(top, #333333, #222222); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); background-image: -webkit-linear-gradient(top, #333333, #222222); background-image: -o-linear-gradient(top, #333333, #222222); background-image: linear-gradient(top, #333333, #222222); background-repeat: repeat-x; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); } .navbar .container { width: auto; } .nav-collapse.collapse { height: auto; } .navbar { color: #999999; } .navbar .brand:hover { text-decoration: none; } .navbar .brand { display: block; float: left; padding: 8px 20px 12px; margin-left: -20px; font-size: 20px; font-weight: 200; line-height: 1; color: #999999; } .navbar .navbar-text { margin-bottom: 0; line-height: 40px; } .navbar .navbar-link { color: #999999; } .navbar .navbar-link:hover { color: #ffffff; } .navbar .btn, .navbar .btn-group { margin-top: 5px; } .navbar .btn-group .btn { margin: 0; } .navbar-form { margin-bottom: 0; *zoom: 1; } .navbar-form:before, .navbar-form:after { display: table; content: ""; } .navbar-form:after { clear: both; } .navbar-form input, .navbar-form select, .navbar-form .radio, .navbar-form .checkbox { margin-top: 5px; } .navbar-form input, .navbar-form select { display: inline-block; margin-bottom: 0; } .navbar-form input[type="image"], .navbar-form input[type="checkbox"], .navbar-form input[type="radio"] { margin-top: 3px; } .navbar-form .input-append, .navbar-form .input-prepend { margin-top: 6px; white-space: nowrap; } .navbar-form .input-append input, .navbar-form .input-prepend input { margin-top: 0; } .navbar-search { position: relative; float: left; margin-top: 6px; margin-bottom: 0; } .navbar-search .search-query { padding: 4px 9px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; line-height: 1; color: #ffffff; background-color: #626262; border: 1px solid #151515; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); -webkit-transition: none; -moz-transition: none; -ms-transition: none; -o-transition: none; transition: none; } .navbar-search .search-query:-moz-placeholder { color: #cccccc; } .navbar-search .search-query:-ms-input-placeholder { color: #cccccc; } .navbar-search .search-query::-webkit-input-placeholder { color: #cccccc; } .navbar-search .search-query:focus, .navbar-search .search-query.focused { padding: 5px 10px; color: #333333; text-shadow: 0 1px 0 #ffffff; background-color: #ffffff; border: 0; outline: 0; -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; margin-bottom: 0; } .navbar-fixed-top .navbar-inner, .navbar-fixed-bottom .navbar-inner { padding-right: 0; padding-left: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } .navbar-fixed-top .container, .navbar-fixed-bottom .container { width: 940px; } .navbar-fixed-top { top: 0; } .navbar-fixed-bottom { bottom: 0; } .navbar .nav { position: relative; left: 0; display: block; float: left; margin: 0 10px 0 0; } .navbar .nav.pull-right { float: right; } .navbar .nav > li { display: block; float: left; } .navbar .nav > li > a { float: none; padding: 9px 10px 11px; line-height: 19px; color: #999999; text-decoration: none; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .navbar .btn { display: inline-block; padding: 4px 10px 4px; margin: 5px 5px 6px; line-height: 18px; } .navbar .btn-group { padding: 5px 5px 6px; margin: 0; } .navbar .nav > li > a:hover { color: #ffffff; text-decoration: none; background-color: transparent; } .navbar .nav .active > a, .navbar .nav .active > a:hover { color: #ffffff; text-decoration: none; background-color: #222222; } .navbar .divider-vertical { width: 1px; height: 40px; margin: 0 9px; overflow: hidden; background-color: #222222; border-right: 1px solid #333333; } .navbar .nav.pull-right { margin-right: 0; margin-left: 10px; } .navbar .btn-navbar { display: none; float: right; padding: 7px 10px; margin-right: 5px; margin-left: 5px; background-color: #2c2c2c; *background-color: #222222; background-image: -ms-linear-gradient(top, #333333, #222222); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); background-image: -webkit-linear-gradient(top, #333333, #222222); background-image: -o-linear-gradient(top, #333333, #222222); background-image: linear-gradient(top, #333333, #222222); background-image: -moz-linear-gradient(top, #333333, #222222); background-repeat: repeat-x; border-color: #222222 #222222 #000000; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); } .navbar .btn-navbar:hover, .navbar .btn-navbar:active, .navbar .btn-navbar.active, .navbar .btn-navbar.disabled, .navbar .btn-navbar[disabled] { background-color: #222222; *background-color: #151515; } .navbar .btn-navbar:active, .navbar .btn-navbar.active { background-color: #080808 \9; } .navbar .btn-navbar .icon-bar { display: block; width: 18px; height: 2px; background-color: #f5f5f5; -webkit-border-radius: 1px; -moz-border-radius: 1px; border-radius: 1px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); } .btn-navbar .icon-bar + .icon-bar { margin-top: 3px; } .navbar .dropdown-menu:before { position: absolute; top: -7px; left: 9px; display: inline-block; border-right: 7px solid transparent; border-bottom: 7px solid #ccc; border-left: 7px solid transparent; border-bottom-color: rgba(0, 0, 0, 0.2); content: ''; } .navbar .dropdown-menu:after { position: absolute; top: -6px; left: 10px; display: inline-block; border-right: 6px solid transparent; border-bottom: 6px solid #ffffff; border-left: 6px solid transparent; content: ''; } .navbar-fixed-bottom .dropdown-menu:before { top: auto; bottom: -7px; border-top: 7px solid #ccc; border-bottom: 0; border-top-color: rgba(0, 0, 0, 0.2); } .navbar-fixed-bottom .dropdown-menu:after { top: auto; bottom: -6px; border-top: 6px solid #ffffff; border-bottom: 0; } .navbar .nav li.dropdown .dropdown-toggle .caret, .navbar .nav li.dropdown.open .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; } .navbar .nav li.dropdown.active .caret { opacity: 1; filter: alpha(opacity=100); } .navbar .nav li.dropdown.open > .dropdown-toggle, .navbar .nav li.dropdown.active > .dropdown-toggle, .navbar .nav li.dropdown.open.active > .dropdown-toggle { background-color: transparent; } .navbar .nav li.dropdown.active > .dropdown-toggle:hover { color: #ffffff; } .navbar .pull-right .dropdown-menu, .navbar .dropdown-menu.pull-right { right: 0; left: auto; } .navbar .pull-right .dropdown-menu:before, .navbar .dropdown-menu.pull-right:before { right: 12px; left: auto; } .navbar .pull-right .dropdown-menu:after, .navbar .dropdown-menu.pull-right:after { right: 13px; left: auto; } .breadcrumb { padding: 7px 14px; margin: 0 0 18px; list-style: none; background-color: #fbfbfb; background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5)); background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); background-image: linear-gradient(top, #ffffff, #f5f5f5); background-repeat: repeat-x; border: 1px solid #ddd; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); -webkit-box-shadow: inset 0 1px 0 #ffffff; -moz-box-shadow: inset 0 1px 0 #ffffff; box-shadow: inset 0 1px 0 #ffffff; } .breadcrumb li { display: inline-block; *display: inline; text-shadow: 0 1px 0 #ffffff; *zoom: 1; } .breadcrumb .divider { padding: 0 5px; color: #999999; } .breadcrumb .active a { color: #333333; } .pagination { height: 36px; margin: 18px 0; } .pagination ul { display: inline-block; *display: inline; margin-bottom: 0; margin-left: 0; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; *zoom: 1; -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } .pagination li { display: inline; } .pagination a { float: left; padding: 0 14px; line-height: 34px; text-decoration: none; border: 1px solid #ddd; border-left-width: 0; } .pagination a:hover, .pagination .active a { background-color: #f5f5f5; } .pagination .active a { color: #999999; cursor: default; } .pagination .disabled span, .pagination .disabled a, .pagination .disabled a:hover { color: #999999; cursor: default; background-color: transparent; } .pagination li:first-child a { border-left-width: 1px; -webkit-border-radius: 3px 0 0 3px; -moz-border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px; } .pagination li:last-child a { -webkit-border-radius: 0 3px 3px 0; -moz-border-radius: 0 3px 3px 0; border-radius: 0 3px 3px 0; } .pagination-centered { text-align: center; } .pagination-right { text-align: right; } .pager { margin-bottom: 18px; margin-left: 0; text-align: center; list-style: none; *zoom: 1; } .pager:before, .pager:after { display: table; content: ""; } .pager:after { clear: both; } .pager li { display: inline; } .pager a { display: inline-block; padding: 5px 14px; background-color: #fff; border: 1px solid #ddd; -webkit-border-radius: 15px; -moz-border-radius: 15px; border-radius: 15px; } .pager a:hover { text-decoration: none; background-color: #f5f5f5; } .pager .next a { float: right; } .pager .previous a { float: left; } .pager .disabled a, .pager .disabled a:hover { color: #999999; cursor: default; background-color: #fff; } .modal-open .dropdown-menu { z-index: 2050; } .modal-open .dropdown.open { *z-index: 2050; } .modal-open .popover { z-index: 2060; } .modal-open .tooltip { z-index: 2070; } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; background-color: #000000; } .modal-backdrop.fade { opacity: 0; } .modal-backdrop, .modal-backdrop.fade.in { opacity: 0.8; filter: alpha(opacity=80); } .modal { position: fixed; top: 50%; left: 50%; z-index: 1050; width: 560px; margin: -250px 0 0 -280px; overflow: auto; background-color: #ffffff; border: 1px solid #999; border: 1px solid rgba(0, 0, 0, 0.3); *border: 1px solid #999; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -webkit-background-clip: padding-box; -moz-background-clip: padding-box; background-clip: padding-box; } .modal.fade { top: -25%; -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; -moz-transition: opacity 0.3s linear, top 0.3s ease-out; -ms-transition: opacity 0.3s linear, top 0.3s ease-out; -o-transition: opacity 0.3s linear, top 0.3s ease-out; transition: opacity 0.3s linear, top 0.3s ease-out; } .modal.fade.in { top: 50%; } .modal-header { padding: 9px 15px; border-bottom: 1px solid #eee; } .modal-header .close { margin-top: 2px; } .modal-body { max-height: 400px; padding: 15px; overflow-y: auto; } .modal-form { margin-bottom: 0; } .modal-footer { padding: 14px 15px 15px; margin-bottom: 0; text-align: right; background-color: #f5f5f5; border-top: 1px solid #ddd; -webkit-border-radius: 0 0 6px 6px; -moz-border-radius: 0 0 6px 6px; border-radius: 0 0 6px 6px; *zoom: 1; -webkit-box-shadow: inset 0 1px 0 #ffffff; -moz-box-shadow: inset 0 1px 0 #ffffff; box-shadow: inset 0 1px 0 #ffffff; } .modal-footer:before, .modal-footer:after { display: table; content: ""; } .modal-footer:after { clear: both; } .modal-footer .btn + .btn { margin-bottom: 0; margin-left: 5px; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .tooltip { position: absolute; z-index: 1020; display: block; padding: 5px; font-size: 11px; opacity: 0; filter: alpha(opacity=0); visibility: visible; } .tooltip.in { opacity: 0.8; filter: alpha(opacity=80); } .tooltip.top { margin-top: -2px; } .tooltip.right { margin-left: 2px; } .tooltip.bottom { margin-top: 2px; } .tooltip.left { margin-left: -2px; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top: 5px solid #000000; border-right: 5px solid transparent; border-left: 5px solid transparent; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 5px solid #000000; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-right: 5px solid transparent; border-bottom: 5px solid #000000; border-left: 5px solid transparent; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-top: 5px solid transparent; border-right: 5px solid #000000; border-bottom: 5px solid transparent; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #ffffff; text-align: center; text-decoration: none; background-color: #000000; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; } .popover { position: absolute; top: 0; left: 0; z-index: 1010; display: none; padding: 5px; } .popover.top { margin-top: -5px; } .popover.right { margin-left: 5px; } .popover.bottom { margin-top: 5px; } .popover.left { margin-left: -5px; } .popover.top .arrow { bottom: 0; left: 50%; margin-left: -5px; border-top: 5px solid #000000; border-right: 5px solid transparent; border-left: 5px solid transparent; } .popover.right .arrow { top: 50%; left: 0; margin-top: -5px; border-top: 5px solid transparent; border-right: 5px solid #000000; border-bottom: 5px solid transparent; } .popover.bottom .arrow { top: 0; left: 50%; margin-left: -5px; border-right: 5px solid transparent; border-bottom: 5px solid #000000; border-left: 5px solid transparent; } .popover.left .arrow { top: 50%; right: 0; margin-top: -5px; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 5px solid #000000; } .popover .arrow { position: absolute; width: 0; height: 0; } .popover-inner { width: 280px; padding: 3px; overflow: hidden; background: #000000; background: rgba(0, 0, 0, 0.8); -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); } .popover-title { padding: 9px 15px; line-height: 1; background-color: #f5f5f5; border-bottom: 1px solid #eee; -webkit-border-radius: 3px 3px 0 0; -moz-border-radius: 3px 3px 0 0; border-radius: 3px 3px 0 0; } .popover-content { padding: 14px; background-color: #ffffff; -webkit-border-radius: 0 0 3px 3px; -moz-border-radius: 0 0 3px 3px; border-radius: 0 0 3px 3px; -webkit-background-clip: padding-box; -moz-background-clip: padding-box; background-clip: padding-box; } .popover-content p, .popover-content ul, .popover-content ol { margin-bottom: 0; } .thumbnails { margin-left: -20px; list-style: none; *zoom: 1; } .thumbnails:before, .thumbnails:after { display: table; content: ""; } .thumbnails:after { clear: both; } .row-fluid .thumbnails { margin-left: 0; } .thumbnails > li { float: left; margin-bottom: 18px; margin-left: 20px; } .thumbnail { display: block; padding: 4px; line-height: 1; border: 1px solid #ddd; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); } a.thumbnail:hover { border-color: #0088cc; -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); } .thumbnail > img { display: block; max-width: 100%; margin-right: auto; margin-left: auto; } .thumbnail .caption { padding: 9px; } .label, .badge { font-size: 10.998px; font-weight: bold; line-height: 14px; color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); white-space: nowrap; vertical-align: baseline; background-color: #999999; } .label { padding: 1px 4px 2px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .badge { padding: 1px 9px 2px; -webkit-border-radius: 9px; -moz-border-radius: 9px; border-radius: 9px; } a.label:hover, a.badge:hover { color: #ffffff; text-decoration: none; cursor: pointer; } .label-important, .badge-important { background-color: #b94a48; } .label-important[href], .badge-important[href] { background-color: #953b39; } .label-warning, .badge-warning { background-color: #f89406; } .label-warning[href], .badge-warning[href] { background-color: #c67605; } .label-success, .badge-success { background-color: #468847; } .label-success[href], .badge-success[href] { background-color: #356635; } .label-info, .badge-info { background-color: #3a87ad; } .label-info[href], .badge-info[href] { background-color: #2d6987; } .label-inverse, .badge-inverse { background-color: #333333; } .label-inverse[href], .badge-inverse[href] { background-color: #1a1a1a; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-moz-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-ms-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-o-keyframes progress-bar-stripes { from { background-position: 0 0; } to { background-position: 40px 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { height: 18px; margin-bottom: 18px; overflow: hidden; background-color: #f7f7f7; background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); background-image: linear-gradient(top, #f5f5f5, #f9f9f9); background-repeat: repeat-x; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0); -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } .progress .bar { width: 0; height: 18px; font-size: 12px; color: #ffffff; text-align: center; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #0e90d2; background-image: -moz-linear-gradient(top, #149bdf, #0480be); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); background-image: -webkit-linear-gradient(top, #149bdf, #0480be); background-image: -o-linear-gradient(top, #149bdf, #0480be); background-image: linear-gradient(top, #149bdf, #0480be); background-image: -ms-linear-gradient(top, #149bdf, #0480be); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0); -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; -webkit-transition: width 0.6s ease; -moz-transition: width 0.6s ease; -ms-transition: width 0.6s ease; -o-transition: width 0.6s ease; transition: width 0.6s ease; } .progress-striped .bar { background-color: #149bdf; background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); -webkit-background-size: 40px 40px; -moz-background-size: 40px 40px; -o-background-size: 40px 40px; background-size: 40px 40px; } .progress.active .bar { -webkit-animation: progress-bar-stripes 2s linear infinite; -moz-animation: progress-bar-stripes 2s linear infinite; -ms-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-danger .bar { background-color: #dd514c; background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); background-image: linear-gradient(top, #ee5f5b, #c43c35); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); } .progress-danger.progress-striped .bar { background-color: #ee5f5b; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-success .bar { background-color: #5eb95e; background-image: -moz-linear-gradient(top, #62c462, #57a957); background-image: -ms-linear-gradient(top, #62c462, #57a957); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); background-image: -webkit-linear-gradient(top, #62c462, #57a957); background-image: -o-linear-gradient(top, #62c462, #57a957); background-image: linear-gradient(top, #62c462, #57a957); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); } .progress-success.progress-striped .bar { background-color: #62c462; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-info .bar { background-color: #4bb1cf; background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); background-image: -o-linear-gradient(top, #5bc0de, #339bb9); background-image: linear-gradient(top, #5bc0de, #339bb9); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); } .progress-info.progress-striped .bar { background-color: #5bc0de; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-warning .bar { background-color: #faa732; background-image: -moz-linear-gradient(top, #fbb450, #f89406); background-image: -ms-linear-gradient(top, #fbb450, #f89406); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); background-image: -webkit-linear-gradient(top, #fbb450, #f89406); background-image: -o-linear-gradient(top, #fbb450, #f89406); background-image: linear-gradient(top, #fbb450, #f89406); background-repeat: repeat-x; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0); } .progress-warning.progress-striped .bar { background-color: #fbb450; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .accordion { margin-bottom: 18px; } .accordion-group { margin-bottom: 2px; border: 1px solid #e5e5e5; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .accordion-heading { border-bottom: 0; } .accordion-heading .accordion-toggle { display: block; padding: 8px 15px; } .accordion-toggle { cursor: pointer; } .accordion-inner { padding: 9px 15px; border-top: 1px solid #e5e5e5; } .carousel { position: relative; margin-bottom: 18px; line-height: 1; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel .item { position: relative; display: none; -webkit-transition: 0.6s ease-in-out left; -moz-transition: 0.6s ease-in-out left; -ms-transition: 0.6s ease-in-out left; -o-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel .item > img { display: block; line-height: 1; } .carousel .active, .carousel .next, .carousel .prev { display: block; } .carousel .active { left: 0; } .carousel .next, .carousel .prev { position: absolute; top: 0; width: 100%; } .carousel .next { left: 100%; } .carousel .prev { left: -100%; } .carousel .next.left, .carousel .prev.right { left: 0; } .carousel .active.left { left: -100%; } .carousel .active.right { left: 100%; } .carousel-control { position: absolute; top: 40%; left: 15px; width: 40px; height: 40px; margin-top: -20px; font-size: 60px; font-weight: 100; line-height: 30px; color: #ffffff; text-align: center; background: #222222; border: 3px solid #ffffff; -webkit-border-radius: 23px; -moz-border-radius: 23px; border-radius: 23px; opacity: 0.5; filter: alpha(opacity=50); } .carousel-control.right { right: 15px; left: auto; } .carousel-control:hover { color: #ffffff; text-decoration: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-caption { position: absolute; right: 0; bottom: 0; left: 0; padding: 10px 15px 5px; background: #333333; background: rgba(0, 0, 0, 0.75); } .carousel-caption h4, .carousel-caption p { color: #ffffff; } .hero-unit { padding: 60px; margin-bottom: 30px; background-color: #eeeeee; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; } .hero-unit h1 { margin-bottom: 0; font-size: 60px; line-height: 1; letter-spacing: -1px; color: inherit; } .hero-unit p { font-size: 18px; font-weight: 200; line-height: 27px; color: inherit; } .pull-right { float: right; } .pull-left { float: left; } .hide { display: none; } .show { display: block; } .invisible { visibility: hidden; } ================================================ FILE: PaiZhiCheng/src/main/resources/static/index.html ================================================ spring-boot-demo-websocket-socketio

spring-boot-demo-websocket-socketio


================================================ FILE: PaiZhiCheng/src/main/resources/static/index2.html ================================================ spring-boot-demo-websocket-socketio

spring-boot-demo-websocket-socketio


================================================ FILE: PaiZhiCheng/src/main/resources/static/js/socket.io/socket.io.js ================================================ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.io=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&!this.encoding){var pack=this.packetBuffer.shift();this.packet(pack)}};Manager.prototype.cleanup=function(){var sub;while(sub=this.subs.shift())sub.destroy();this.packetBuffer=[];this.encoding=false;this.decoder.destroy()};Manager.prototype.close=Manager.prototype.disconnect=function(){this.skipReconnect=true;this.backoff.reset();this.readyState="closed";this.engine&&this.engine.close()};Manager.prototype.onclose=function(reason){debug("close");this.cleanup();this.backoff.reset();this.readyState="closed";this.emit("close",reason);if(this._reconnection&&!this.skipReconnect){this.reconnect()}};Manager.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var self=this;if(this.backoff.attempts>=this._reconnectionAttempts){debug("reconnect failed");this.backoff.reset();this.emitAll("reconnect_failed");this.reconnecting=false}else{var delay=this.backoff.duration();debug("will wait %dms before reconnect attempt",delay);this.reconnecting=true;var timer=setTimeout(function(){if(self.skipReconnect)return;debug("attempting reconnect");self.emitAll("reconnect_attempt",self.backoff.attempts);self.emitAll("reconnecting",self.backoff.attempts);if(self.skipReconnect)return;self.open(function(err){if(err){debug("reconnect attempt error");self.reconnecting=false;self.reconnect();self.emitAll("reconnect_error",err.data)}else{debug("reconnect success");self.onreconnect()}})},delay);this.subs.push({destroy:function(){clearTimeout(timer)}})}};Manager.prototype.onreconnect=function(){var attempt=this.backoff.attempts;this.reconnecting=false;this.backoff.reset();this.updateSocketIds();this.emitAll("reconnect",attempt)}},{"./on":4,"./socket":5,"./url":6,backo2:7,"component-bind":8,"component-emitter":9,debug:10,"engine.io-client":11,indexof:42,"object-component":43,"socket.io-parser":46}],4:[function(_dereq_,module,exports){module.exports=on;function on(obj,ev,fn){obj.on(ev,fn);return{destroy:function(){obj.removeListener(ev,fn)}}}},{}],5:[function(_dereq_,module,exports){var parser=_dereq_("socket.io-parser");var Emitter=_dereq_("component-emitter");var toArray=_dereq_("to-array");var on=_dereq_("./on");var bind=_dereq_("component-bind");var debug=_dereq_("debug")("socket.io-client:socket");var hasBin=_dereq_("has-binary");module.exports=exports=Socket;var events={connect:1,connect_error:1,connect_timeout:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1};var emit=Emitter.prototype.emit;function Socket(io,nsp){this.io=io;this.nsp=nsp;this.json=this;this.ids=0;this.acks={};if(this.io.autoConnect)this.open();this.receiveBuffer=[];this.sendBuffer=[];this.connected=false;this.disconnected=true}Emitter(Socket.prototype);Socket.prototype.subEvents=function(){if(this.subs)return;var io=this.io;this.subs=[on(io,"open",bind(this,"onopen")),on(io,"packet",bind(this,"onpacket")),on(io,"close",bind(this,"onclose"))]};Socket.prototype.open=Socket.prototype.connect=function(){if(this.connected)return this;this.subEvents();this.io.open();if("open"==this.io.readyState)this.onopen();return this};Socket.prototype.send=function(){var args=toArray(arguments);args.unshift("message");this.emit.apply(this,args);return this};Socket.prototype.emit=function(ev){if(events.hasOwnProperty(ev)){emit.apply(this,arguments);return this}var args=toArray(arguments);var parserType=parser.EVENT;if(hasBin(args)){parserType=parser.BINARY_EVENT}var packet={type:parserType,data:args};if("function"==typeof args[args.length-1]){debug("emitting packet with ack id %d",this.ids);this.acks[this.ids]=args.pop();packet.id=this.ids++}if(this.connected){this.packet(packet)}else{this.sendBuffer.push(packet)}return this};Socket.prototype.packet=function(packet){packet.nsp=this.nsp;this.io.packet(packet)};Socket.prototype.onopen=function(){debug("transport is open - connecting");if("/"!=this.nsp){this.packet({type:parser.CONNECT})}};Socket.prototype.onclose=function(reason){debug("close (%s)",reason);this.connected=false;this.disconnected=true;delete this.id;this.emit("disconnect",reason)};Socket.prototype.onpacket=function(packet){if(packet.nsp!=this.nsp)return;switch(packet.type){case parser.CONNECT:this.onconnect();break;case parser.EVENT:this.onevent(packet);break;case parser.BINARY_EVENT:this.onevent(packet);break;case parser.ACK:this.onack(packet);break;case parser.BINARY_ACK:this.onack(packet);break;case parser.DISCONNECT:this.ondisconnect();break;case parser.ERROR:this.emit("error",packet.data);break}};Socket.prototype.onevent=function(packet){var args=packet.data||[];debug("emitting event %j",args);if(null!=packet.id){debug("attaching ack callback to event");args.push(this.ack(packet.id))}if(this.connected){emit.apply(this,args)}else{this.receiveBuffer.push(args)}};Socket.prototype.ack=function(id){var self=this;var sent=false;return function(){if(sent)return;sent=true;var args=toArray(arguments);debug("sending ack %j",args);var type=hasBin(args)?parser.BINARY_ACK:parser.ACK;self.packet({type:type,id:id,data:args})}};Socket.prototype.onack=function(packet){debug("calling ack %s with %j",packet.id,packet.data);var fn=this.acks[packet.id];fn.apply(this,packet.data);delete this.acks[packet.id]};Socket.prototype.onconnect=function(){this.connected=true;this.disconnected=false;this.emit("connect");this.emitBuffered()};Socket.prototype.emitBuffered=function(){var i;for(i=0;i0&&opts.jitter<=1?opts.jitter:0;this.attempts=0}Backoff.prototype.duration=function(){var ms=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var rand=Math.random();var deviation=Math.floor(rand*this.jitter*ms);ms=(Math.floor(rand*10)&1)==0?ms-deviation:ms+deviation}return Math.min(ms,this.max)|0};Backoff.prototype.reset=function(){this.attempts=0};Backoff.prototype.setMin=function(min){this.ms=min};Backoff.prototype.setMax=function(max){this.max=max};Backoff.prototype.setJitter=function(jitter){this.jitter=jitter}},{}],8:[function(_dereq_,module,exports){var slice=[].slice;module.exports=function(obj,fn){if("string"==typeof fn)fn=obj[fn];if("function"!=typeof fn)throw new Error("bind() requires a function");var args=slice.call(arguments,2);return function(){return fn.apply(obj,args.concat(slice.call(arguments)))}}},{}],9:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks[event]=this._callbacks[event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){var self=this;this._callbacks=this._callbacks||{};function on(){self.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks[event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks[event];return this}var cb;for(var i=0;i=hour)return(ms/hour).toFixed(1)+"h";if(ms>=min)return(ms/min).toFixed(1)+"m";if(ms>=sec)return(ms/sec|0)+"s";return ms+"ms"};debug.enabled=function(name){for(var i=0,len=debug.skips.length;i';iframe=document.createElement(html)}catch(e){iframe=document.createElement("iframe");iframe.name=self.iframeId;iframe.src="javascript:0"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,"\\\n");this.area.value=data.replace(rNewline,"\\n");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState=="complete"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling":18,"component-inherit":21}],17:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest");var Polling=_dereq_("./polling");var Emitter=_dereq_("component-emitter");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling-xhr");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!=="string"&&data!==undefined;var req=this.request({method:"POST",data:data,isBinary:isBinary});var self=this;req.on("success",fn);req.on("error",function(err){self.onError("xhr post error",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug("xhr poll");var req=this.request();var self=this;req.on("data",function(data){self.onData(data)});req.on("error",function(err){self.onError("xhr poll error",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||"GET";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var opts={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;var xhr=this.xhr=new XMLHttpRequest(opts);var self=this;try{debug("xhr open %s: %s",this.method,this.uri);xhr.open(this.method,this.uri,this.async);if(this.supportsBinary){xhr.responseType="arraybuffer"}if("POST"==this.method){try{if(this.isBinary){xhr.setRequestHeader("Content-type","application/octet-stream")}else{xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")}}catch(e){}}if("withCredentials"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug("xhr data %s",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit("success");this.cleanup()};Request.prototype.onData=function(data){this.emit("data",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit("error",err);this.cleanup(true)};Request.prototype.cleanup=function(fromError){if("undefined"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}if(fromError){try{this.xhr.abort()}catch(e){}}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{var contentType;try{contentType=this.xhr.getResponseHeader("Content-Type").split(";")[0]}catch(e){}if(contentType==="application/octet-stream"){data=this.xhr.response}else{if(!this.supportsBinary){data=this.xhr.responseText}else{data="ok"}}}catch(e){this.onError(e)}if(null!=data){this.onData(data)}};Request.prototype.hasXDR=function(){return"undefined"!==typeof global.XDomainRequest&&!this.xs&&this.enablesXDR};Request.prototype.abort=function(){this.cleanup()};if(global.document){Request.requestsCount=0;Request.requests={};if(global.attachEvent){global.attachEvent("onunload",unloadHandler)}else if(global.addEventListener){global.addEventListener("beforeunload",unloadHandler,false)}}function unloadHandler(){for(var i in Request.requests){if(Request.requests.hasOwnProperty(i)){Request.requests[i].abort()}}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling":18,"component-emitter":9,"component-inherit":21,debug:22,xmlhttprequest:20}],18:[function(_dereq_,module,exports){var Transport=_dereq_("../transport");var parseqs=_dereq_("parseqs");var parser=_dereq_("engine.io-parser");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling");module.exports=Polling;var hasXHR2=function(){var XMLHttpRequest=_dereq_("xmlhttprequest");var xhr=new XMLHttpRequest({xdomain:false});return null!=xhr.responseType}();function Polling(opts){var forceBase64=opts&&opts.forceBase64;if(!hasXHR2||forceBase64){this.supportsBinary=false}Transport.call(this,opts)}inherit(Polling,Transport);Polling.prototype.name="polling";Polling.prototype.doOpen=function(){this.poll()};Polling.prototype.pause=function(onPause){var pending=0;var self=this;this.readyState="pausing";function pause(){debug("paused");self.readyState="paused";onPause()}if(this.polling||!this.writable){var total=0;if(this.polling){debug("we are currently polling - waiting to pause");total++;this.once("pollComplete",function(){debug("pre-pause polling complete");--total||pause()})}if(!this.writable){debug("we are currently writing - waiting to pause");total++;this.once("drain",function(){debug("pre-pause writing complete");--total||pause()})}}else{pause()}};Polling.prototype.poll=function(){debug("polling");this.polling=true;this.doPoll();this.emit("poll")};Polling.prototype.onData=function(data){var self=this;debug("polling got data %s",data);var callback=function(packet,index,total){if("opening"==self.readyState){self.onOpen()}if("close"==packet.type){self.onClose();return false}self.onPacket(packet)};parser.decodePayload(data,this.socket.binaryType,callback);if("closed"!=this.readyState){this.polling=false;this.emit("pollComplete");if("open"==this.readyState){this.poll()}else{debug('ignoring poll - transport state "%s"',this.readyState)}}};Polling.prototype.doClose=function(){var self=this;function close(){debug("writing close packet");self.write([{type:"close"}])}if("open"==this.readyState){debug("transport open - closing");close()}else{debug("transport not open - deferring close");this.once("open",close)}};Polling.prototype.write=function(packets){var self=this;this.writable=false;var callbackfn=function(){self.writable=true;self.emit("drain")};var self=this;parser.encodePayload(packets,this.supportsBinary,function(data){self.doWrite(data,callbackfn)})};Polling.prototype.uri=function(){var query=this.query||{};var schema=this.secure?"https":"http";var port="";if(false!==this.timestampRequests){query[this.timestampParam]=+new Date+"-"+Transport.timestamps++}if(!this.supportsBinary&&!query.sid){query.b64=1}query=parseqs.encode(query);if(this.port&&("https"==schema&&this.port!=443||"http"==schema&&this.port!=80)){port=":"+this.port}if(query.length){query="?"+query}return schema+"://"+this.hostname+port+this.path+query}},{"../transport":14,"component-inherit":21,debug:22,"engine.io-parser":25,parseqs:35,xmlhttprequest:20}],19:[function(_dereq_,module,exports){var Transport=_dereq_("../transport");var parser=_dereq_("engine.io-parser");var parseqs=_dereq_("parseqs");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:websocket");var WebSocket=_dereq_("ws");module.exports=WS;function WS(opts){var forceBase64=opts&&opts.forceBase64;if(forceBase64){this.supportsBinary=false}this.perMessageDeflate=opts.perMessageDeflate;Transport.call(this,opts)}inherit(WS,Transport);WS.prototype.name="websocket";WS.prototype.supportsBinary=true;WS.prototype.doOpen=function(){if(!this.check()){return}var self=this;var uri=this.uri();var protocols=void 0;var opts={agent:this.agent,perMessageDeflate:this.perMessageDeflate};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;this.ws=new WebSocket(uri,protocols,opts);if(this.ws.binaryType===undefined){this.supportsBinary=false}this.ws.binaryType="arraybuffer";this.addEventListeners()};WS.prototype.addEventListeners=function(){var self=this;this.ws.onopen=function(){self.onOpen()};this.ws.onclose=function(){self.onClose()};this.ws.onmessage=function(ev){self.onData(ev.data)};this.ws.onerror=function(e){self.onError("websocket error",e)}};if("undefined"!=typeof navigator&&/iPad|iPhone|iPod/i.test(navigator.userAgent)){WS.prototype.onData=function(data){var self=this;setTimeout(function(){Transport.prototype.onData.call(self,data)},0)}}WS.prototype.write=function(packets){var self=this;this.writable=false;for(var i=0,l=packets.length;i=31}exports.formatters.j=function(v){return JSON.stringify(v)};function formatArgs(){var args=arguments;var useColors=this.useColors;args[0]=(useColors?"%c":"")+this.namespace+(useColors?" %c":" ")+args[0]+(useColors?"%c ":" ")+"+"+exports.humanize(this.diff);if(!useColors)return args;var c="color: "+this.color;args=[args[0],c,"color: inherit"].concat(Array.prototype.slice.call(args,1));var index=0;var lastC=0;args[0].replace(/%[a-z%]/g,function(match){if("%"===match)return;index++;if("%c"===match){lastC=index}});args.splice(lastC,0,c);return args}function log(){return"object"==typeof console&&"function"==typeof console.log&&Function.prototype.apply.call(console.log,console,arguments)}function save(namespaces){try{if(null==namespaces){localStorage.removeItem("debug")}else{localStorage.debug=namespaces}}catch(e){}}function load(){var r;try{r=localStorage.debug}catch(e){}return r}exports.enable(load())},{"./debug":23}],23:[function(_dereq_,module,exports){exports=module.exports=debug;exports.coerce=coerce;exports.disable=disable;exports.enable=enable;exports.enabled=enabled;exports.humanize=_dereq_("ms");exports.names=[];exports.skips=[];exports.formatters={};var prevColor=0;var prevTime;function selectColor(){return exports.colors[prevColor++%exports.colors.length]}function debug(namespace){function disabled(){}disabled.enabled=false;function enabled(){var self=enabled;var curr=+new Date;var ms=curr-(prevTime||curr);self.diff=ms;self.prev=prevTime;self.curr=curr;prevTime=curr;if(null==self.useColors)self.useColors=exports.useColors();if(null==self.color&&self.useColors)self.color=selectColor();var args=Array.prototype.slice.call(arguments);args[0]=exports.coerce(args[0]);if("string"!==typeof args[0]){args=["%o"].concat(args)}var index=0;args[0]=args[0].replace(/%([a-z%])/g,function(match,format){if(match==="%")return match;index++;var formatter=exports.formatters[format];if("function"===typeof formatter){var val=args[index];match=formatter.call(self,val);args.splice(index,1);index--}return match});if("function"===typeof exports.formatArgs){args=exports.formatArgs.apply(self,args)}var logFn=enabled.log||exports.log||console.log.bind(console);logFn.apply(self,args)}enabled.enabled=true;var fn=exports.enabled(namespace)?enabled:disabled;fn.namespace=namespace;return fn}function enable(namespaces){exports.save(namespaces);var split=(namespaces||"").split(/[\s,]+/);var len=split.length;for(var i=0;i=d)return Math.round(ms/d)+"d";if(ms>=h)return Math.round(ms/h)+"h";if(ms>=m)return Math.round(ms/m)+"m";if(ms>=s)return Math.round(ms/s)+"s";return ms+"ms"}function long(ms){return plural(ms,d,"day")||plural(ms,h,"hour")||plural(ms,m,"minute")||plural(ms,s,"second")||ms+" ms"}function plural(ms,n,name){if(ms1){return{type:packetslist[type],data:data.substring(1)}}else{return{type:packetslist[type]}}}var asArray=new Uint8Array(data);var type=asArray[0];var rest=sliceBuffer(data,1);if(Blob&&binaryType==="blob"){rest=new Blob([rest])}return{type:packetslist[type],data:rest}};exports.decodeBase64Packet=function(msg,binaryType){var type=packetslist[msg.charAt(0)];if(!global.ArrayBuffer){return{type:type,data:{base64:true,data:msg.substr(1)}}}var data=base64encoder.decode(msg.substr(1));if(binaryType==="blob"&&Blob){data=new Blob([data])}return{type:type,data:data}};exports.encodePayload=function(packets,supportsBinary,callback){if(typeof supportsBinary=="function"){callback=supportsBinary;supportsBinary=null}var isBinary=hasBinary(packets);if(supportsBinary&&isBinary){if(Blob&&!dontSendBlobs){return exports.encodePayloadAsBlob(packets,callback)}return exports.encodePayloadAsArrayBuffer(packets,callback)}if(!packets.length){return callback("0:")}function setLengthHeader(message){return message.length+":"+message}function encodeOne(packet,doneCallback){exports.encodePacket(packet,!isBinary?false:supportsBinary,true,function(message){doneCallback(null,setLengthHeader(message))})}map(packets,encodeOne,function(err,results){return callback(results.join(""))})};function map(ary,each,done){var result=new Array(ary.length);var next=after(ary.length,done);var eachWithIndex=function(i,el,cb){each(el,function(error,msg){result[i]=msg;cb(error,result)})};for(var i=0;i0){var tailArray=new Uint8Array(bufferTail);var isString=tailArray[0]===0;var msgLength="";for(var i=1;;i++){if(tailArray[i]==255)break;if(msgLength.length>310){numberTooLong=true;break}msgLength+=tailArray[i]}if(numberTooLong)return callback(err,0,1);bufferTail=sliceBuffer(bufferTail,2+msgLength.length);msgLength=parseInt(msgLength);var msg=sliceBuffer(bufferTail,0,msgLength);if(isString){try{msg=String.fromCharCode.apply(null,new Uint8Array(msg))}catch(e){var typed=new Uint8Array(msg);msg="";for(var i=0;ibytes){end=bytes}if(start>=bytes||start>=end||bytes===0){return new ArrayBuffer(0)}var abv=new Uint8Array(arraybuffer);var result=new Uint8Array(end-start);for(var i=start,ii=0;i>2];base64+=chars[(bytes[i]&3)<<4|bytes[i+1]>>4];base64+=chars[(bytes[i+1]&15)<<2|bytes[i+2]>>6];base64+=chars[bytes[i+2]&63]}if(len%3===2){base64=base64.substring(0,base64.length-1)+"="}else if(len%3===1){base64=base64.substring(0,base64.length-2)+"=="}return base64};exports.decode=function(base64){var bufferLength=base64.length*.75,len=base64.length,i,p=0,encoded1,encoded2,encoded3,encoded4;if(base64[base64.length-1]==="="){bufferLength--;if(base64[base64.length-2]==="="){bufferLength--}}var arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer);for(i=0;i>4;bytes[p++]=(encoded2&15)<<4|encoded3>>2;bytes[p++]=(encoded3&3)<<6|encoded4&63}return arraybuffer}})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")},{}],30:[function(_dereq_,module,exports){(function(global){var BlobBuilder=global.BlobBuilder||global.WebKitBlobBuilder||global.MSBlobBuilder||global.MozBlobBuilder;var blobSupported=function(){try{var b=new Blob(["hi"]);return b.size==2}catch(e){return false}}();var blobBuilderSupported=BlobBuilder&&BlobBuilder.prototype.append&&BlobBuilder.prototype.getBlob;function BlobBuilderConstructor(ary,options){options=options||{};var bb=new BlobBuilder;for(var i=0;i=55296&&value<=56319&&counter65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value)}return output}function createByte(codePoint,shift){return stringFromCharCode(codePoint>>shift&63|128)}function encodeCodePoint(codePoint){if((codePoint&4294967168)==0){return stringFromCharCode(codePoint)}var symbol="";if((codePoint&4294965248)==0){symbol=stringFromCharCode(codePoint>>6&31|192)}else if((codePoint&4294901760)==0){symbol=stringFromCharCode(codePoint>>12&15|224);symbol+=createByte(codePoint,6)}else if((codePoint&4292870144)==0){symbol=stringFromCharCode(codePoint>>18&7|240);symbol+=createByte(codePoint,12);symbol+=createByte(codePoint,6)}symbol+=stringFromCharCode(codePoint&63|128);return symbol}function utf8encode(string){var codePoints=ucs2decode(string);var length=codePoints.length;var index=-1;var codePoint;var byteString="";while(++index=byteCount){throw Error("Invalid byte index")}var continuationByte=byteArray[byteIndex]&255;byteIndex++;if((continuationByte&192)==128){return continuationByte&63}throw Error("Invalid continuation byte")}function decodeSymbol(){var byte1;var byte2;var byte3;var byte4;var codePoint;if(byteIndex>byteCount){throw Error("Invalid byte index")}if(byteIndex==byteCount){return false}byte1=byteArray[byteIndex]&255;byteIndex++;if((byte1&128)==0){return byte1}if((byte1&224)==192){var byte2=readContinuationByte();codePoint=(byte1&31)<<6|byte2;if(codePoint>=128){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&240)==224){byte2=readContinuationByte();byte3=readContinuationByte();codePoint=(byte1&15)<<12|byte2<<6|byte3;if(codePoint>=2048){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&248)==240){byte2=readContinuationByte();byte3=readContinuationByte();byte4=readContinuationByte();codePoint=(byte1&15)<<18|byte2<<12|byte3<<6|byte4;if(codePoint>=65536&&codePoint<=1114111){return codePoint}}throw Error("Invalid UTF-8 detected")}var byteArray;var byteCount;var byteIndex;function utf8decode(byteString){byteArray=ucs2decode(byteString);byteCount=byteArray.length;byteIndex=0;var codePoints=[];var tmp;while((tmp=decodeSymbol())!==false){codePoints.push(tmp)}return ucs2encode(codePoints)}var utf8={version:"2.0.0",encode:utf8encode,decode:utf8decode};if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define(function(){return utf8})}else if(freeExports&&!freeExports.nodeType){if(freeModule){freeModule.exports=utf8}else{var object={};var hasOwnProperty=object.hasOwnProperty;for(var key in utf8){hasOwnProperty.call(utf8,key)&&(freeExports[key]=utf8[key])}}}else{root.utf8=utf8}})(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],34:[function(_dereq_,module,exports){(function(global){var rvalidchars=/^[\],:{}\s]*$/;var rvalidescape=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;var rvalidtokens=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;var rvalidbraces=/(?:^|:|,)(?:\s*\[)+/g;var rtrimLeft=/^\s+/;var rtrimRight=/\s+$/;module.exports=function parsejson(data){if("string"!=typeof data||!data){return null}data=data.replace(rtrimLeft,"").replace(rtrimRight,"");if(global.JSON&&JSON.parse){return JSON.parse(data)}if(rvalidchars.test(data.replace(rvalidescape,"@").replace(rvalidtokens,"]").replace(rvalidbraces,""))){return new Function("return "+data)()}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],35:[function(_dereq_,module,exports){exports.encode=function(obj){var str="";for(var i in obj){if(obj.hasOwnProperty(i)){if(str.length)str+="&";str+=encodeURIComponent(i)+"="+encodeURIComponent(obj[i])}}return str};exports.decode=function(qs){var qry={};var pairs=qs.split("&");for(var i=0,l=pairs.length;i1)))/4)-floor((year-1901+month)/100)+floor((year-1601+month)/400)}}if(!(isProperty={}.hasOwnProperty)){isProperty=function(property){var members={},constructor;if((members.__proto__=null,members.__proto__={toString:1},members).toString!=getClass){isProperty=function(property){var original=this.__proto__,result=property in(this.__proto__=null,this);this.__proto__=original;return result}}else{constructor=members.constructor;isProperty=function(property){var parent=(this.constructor||constructor).prototype;return property in this&&!(property in parent&&this[property]===parent[property])}}members=null;return isProperty.call(this,property)}}var PrimitiveTypes={"boolean":1,number:1,string:1,undefined:1};var isHostType=function(object,property){var type=typeof object[property];return type=="object"?!!object[property]:!PrimitiveTypes[type]};forEach=function(object,callback){var size=0,Properties,members,property;(Properties=function(){this.valueOf=0}).prototype.valueOf=0;members=new Properties;for(property in members){if(isProperty.call(members,property)){size++}}Properties=members=null;if(!size){members=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,length;var hasProperty=!isFunction&&typeof object.constructor!="function"&&isHostType(object,"hasOwnProperty")?object.hasOwnProperty:isProperty;for(property in object){if(!(isFunction&&property=="prototype")&&hasProperty.call(object,property)){callback(property)}}for(length=members.length;property=members[--length];hasProperty.call(object,property)&&callback(property));}}else if(size==2){forEach=function(object,callback){var members={},isFunction=getClass.call(object)==functionClass,property;for(property in object){if(!(isFunction&&property=="prototype")&&!isProperty.call(members,property)&&(members[property]=1)&&isProperty.call(object,property)){callback(property)}}}}else{forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,isConstructor;for(property in object){if(!(isFunction&&property=="prototype")&&isProperty.call(object,property)&&!(isConstructor=property==="constructor")){callback(property)}}if(isConstructor||isProperty.call(object,property="constructor")){callback(property)}}}return forEach(object,callback)};if(!has("json-stringify")){var Escapes={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"};var leadingZeroes="000000";var toPaddedString=function(width,value){return(leadingZeroes+(value||0)).slice(-width)};var unicodePrefix="\\u00";var quote=function(value){var result='"',index=0,length=value.length,isLarge=length>10&&charIndexBuggy,symbols;if(isLarge){symbols=value.split("")}for(;index-1/0&&value<1/0){if(getDay){date=floor(value/864e5);for(year=floor(date/365.2425)+1970-1;getDay(year+1,0)<=date;year++);for(month=floor((date-getDay(year,0))/30.42);getDay(year,month+1)<=date;month++);date=1+date-getDay(year,month);time=(value%864e5+864e5)%864e5;hours=floor(time/36e5)%24;minutes=floor(time/6e4)%60;seconds=floor(time/1e3)%60;milliseconds=time%1e3}else{year=value.getUTCFullYear();month=value.getUTCMonth();date=value.getUTCDate();hours=value.getUTCHours();minutes=value.getUTCMinutes();seconds=value.getUTCSeconds();milliseconds=value.getUTCMilliseconds()}value=(year<=0||year>=1e4?(year<0?"-":"+")+toPaddedString(6,year<0?-year:year):toPaddedString(4,year))+"-"+toPaddedString(2,month+1)+"-"+toPaddedString(2,date)+"T"+toPaddedString(2,hours)+":"+toPaddedString(2,minutes)+":"+toPaddedString(2,seconds)+"."+toPaddedString(3,milliseconds)+"Z"}else{value=null}}else if(typeof value.toJSON=="function"&&(className!=numberClass&&className!=stringClass&&className!=arrayClass||isProperty.call(value,"toJSON"))){value=value.toJSON(property)}}if(callback){value=callback.call(object,property,value)}if(value===null){return"null"}className=getClass.call(value);if(className==booleanClass){return""+value}else if(className==numberClass){return value>-1/0&&value<1/0?""+value:"null"}else if(className==stringClass){return quote(""+value)}if(typeof value=="object"){for(length=stack.length;length--;){if(stack[length]===value){throw TypeError()}}stack.push(value);results=[];prefix=indentation;indentation+=whitespace;if(className==arrayClass){for(index=0,length=value.length;index0){for(whitespace="",width>10&&(width=10);whitespace.length=48&&charCode<=57||charCode>=97&&charCode<=102||charCode>=65&&charCode<=70)){abort()}}value+=fromCharCode("0x"+source.slice(begin,Index));break;default:abort()}}else{if(charCode==34){break}charCode=source.charCodeAt(Index);begin=Index;while(charCode>=32&&charCode!=92&&charCode!=34){charCode=source.charCodeAt(++Index)}value+=source.slice(begin,Index)}}if(source.charCodeAt(Index)==34){Index++;return value}abort();default:begin=Index;if(charCode==45){isSigned=true;charCode=source.charCodeAt(++Index)}if(charCode>=48&&charCode<=57){if(charCode==48&&(charCode=source.charCodeAt(Index+1),charCode>=48&&charCode<=57)){abort()}isSigned=false;for(;Index=48&&charCode<=57);Index++);if(source.charCodeAt(Index)==46){position=++Index;for(;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}charCode=source.charCodeAt(Index);if(charCode==101||charCode==69){charCode=source.charCodeAt(++Index);if(charCode==43||charCode==45){Index++}for(position=Index;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}return+source.slice(begin,Index)}if(isSigned){abort()}if(source.slice(Index,Index+4)=="true"){Index+=4;return true}else if(source.slice(Index,Index+5)=="false"){Index+=5;return false}else if(source.slice(Index,Index+4)=="null"){Index+=4;return null}abort()}}return"$"};var get=function(value){var results,hasMembers;if(value=="$"){abort()}if(typeof value=="string"){if((charIndexBuggy?value.charAt(0):value[0])=="@"){return value.slice(1)}if(value=="["){results=[];for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="]"){break}if(hasMembers){if(value==","){value=lex();if(value=="]"){abort()}}else{abort()}}if(value==","){abort()}results.push(get(value))}return results}else if(value=="{"){results={};for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="}"){break}if(hasMembers){if(value==","){value=lex();if(value=="}"){abort()}}else{abort()}}if(value==","||typeof value!="string"||(charIndexBuggy?value.charAt(0):value[0])!="@"||lex()!=":"){abort()}results[value.slice(1)]=get(lex())}return results}abort()}return value};var update=function(source,property,callback){var element=walk(source,property,callback);if(element===undef){delete source[property]}else{source[property]=element}};var walk=function(source,property,callback){var value=source[property],length;if(typeof value=="object"&&value){if(getClass.call(value)==arrayClass){for(length=value.length;length--;){update(value,length,callback)}}else{forEach(value,function(property){update(value,property,callback)})}}return callback.call(source,property,value)};JSON3.parse=function(source,callback){var result,value;Index=0;Source=""+source;result=get(lex());if(lex()!="$"){abort()}Index=Source=null;return callback&&getClass.call(callback)==functionClass?walk((value={},value[""]=result,value),"",callback):result}}}if(isLoader){define(function(){return JSON3})}})(this)},{}],50:[function(_dereq_,module,exports){module.exports=toArray;function toArray(list,index){var array=[];index=index||0;for(var i=index||0;i 电音组队交友小程序-派之城

License

`派之城` 是一个微信音乐组队交友小程序,功能包括:发布音乐节活动、发布组队活动、城市组队匹配、聊天室、音乐播放、个人中心等。 它基于 [`SpringBoot`](https://spring.io/projects/spring-boot)、[`若依`](https://ruoyi.vip/)、[`Vue`](https://cn.vuejs.org/)、[`Uniapp`](https://uniapp.dcloud.io/) 实现。使用了最新的 `SpringBoot` 全家桶技术栈。使用Mysql数据库存储,通过 `SpringBoot` 提供的接口返回数据,前端使用 `Vue` 和 `Uniapp` 实现。 ## 项目预览
## 在线体验 ## 免责声明 本项目仅适用于学习和研究,不得用于商业使用 ## 运行 后端运行: ```bash git clone -b master --single-branch https://github.com/fl1906/music-city.git cd music-city ``` 修改配置: ![img.png](docs/imgs/img.png) ![img_1.png](docs/imgs/img_1.png) ![img_2.png](docs/imgs/img_2.png) ![img_3.png](docs/imgs/img_3.png) 小程序端运行: 打开HbuilderX,导入项目,运行到微信小程序 ```bash git clone -b uniapp --single-branch https://github.com/fl1906/music-city.git cd music-city-uniapp ``` ## 致谢
网易云音乐API : https://binaryify.github.io/NeteaseCloudMusicApi/#/

高校查询接口 : https://hn216.api.yesapi.cn/

若依框架: https://ruoyi.vip/

Uniapp框架: https://uniapp.dcloud.io/

Satoken框架: https://sa-token.dev33.cn/
## 许可协议 [GPL](LICENSE) ## 联系作者 微信:fl1906 ================================================ FILE: pom.xml ================================================ 4.0.0 com.ruoyi ruoyi-vue-plus 4.7.0 RuoYi-Vue-Plus https://gitee.com/dromara/RuoYi-Vue-Plus 派之城后台管理系统 4.7.0 2.7.11 UTF-8 UTF-8 1.8 3.2.2 2.2.2 1.6.15 5.2.3 3.2.1 2.3 1.34.0 3.5.3.1 3.9.1 5.8.18 4.10.0 2.7.10 3.20.1 2.2.3 3.5.2 2.14.2 2.4.0 1.18.26 1.72 2.7.0 1.33 1.12.400 2.0.23 3.1.687 local local debug dev dev debug true prod prod warn cn.hutool hutool-http 5.8.18 org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import cn.hutool hutool-bom ${hutool.version} pom import org.springdoc springdoc-openapi-webmvc-core ${springdoc.version} org.springdoc springdoc-openapi-javadoc ${springdoc.version} org.projectlombok lombok ${lombok.version} org.apache.poi poi ${poi.version} org.apache.poi poi-ooxml ${poi.version} com.alibaba easyexcel ${easyexcel.version} org.apache.poi poi-ooxml-schemas org.apache.velocity velocity-engine-core ${velocity.version} cn.dev33 sa-token-spring-boot-starter ${satoken.version} cn.dev33 sa-token-jwt ${satoken.version} cn.hutool hutool-all com.baomidou dynamic-datasource-spring-boot-starter ${dynamic-ds.version} com.baomidou mybatis-plus-boot-starter ${mybatis-plus.version} p6spy p6spy ${p6spy.version} com.squareup.okhttp3 okhttp ${okhttp.version} com.amazonaws aws-java-sdk-s3 ${aws-java-sdk-s3.version} com.aliyun dysmsapi20170525 ${aliyun.sms.version} com.tencentcloudapi tencentcloud-sdk-java-sms ${tencent.sms.version} de.codecentric spring-boot-admin-starter-server ${spring-boot-admin.version} de.codecentric spring-boot-admin-starter-client ${spring-boot-admin.version} org.redisson redisson-spring-boot-starter ${redisson.version} org.redisson redisson-spring-data-30 org.redisson redisson-spring-data-27 ${redisson.version} com.baomidou lock4j-redisson-spring-boot-starter ${lock4j.version} com.xuxueli xxl-job-core ${xxl-job.version} com.alibaba transmittable-thread-local ${alibaba-ttl.version} org.lionsoul ip2region ${ip2region.version} org.yaml snakeyaml ${snakeyaml.version} org.bouncycastle bcprov-jdk15to18 ${bouncycastle.version} com.ruoyi ruoyi-job ${ruoyi-vue-plus.version} com.ruoyi ruoyi-generator ${ruoyi-vue-plus.version} com.ruoyi ruoyi-framework ${ruoyi-vue-plus.version} com.ruoyi ruoyi-system ${ruoyi-vue-plus.version} com.ruoyi ruoyi-common ${ruoyi-vue-plus.version} com.ruoyi ruoyi-oss ${ruoyi-vue-plus.version} com.ruoyi ruoyi-sms ${ruoyi-vue-plus.version} com.ruoyi pai-zhi-cheng ${ruoyi-vue-plus.version} ruoyi-admin ruoyi-framework ruoyi-system ruoyi-job ruoyi-generator ruoyi-common PaiZhiCheng ruoyi-extend ruoyi-oss ruoyi-sms pom org.apache.maven.plugins maven-compiler-plugin 3.9.0 ${java.version} ${java.version} ${project.build.sourceEncoding} com.github.therapi therapi-runtime-javadoc-scribe 0.15.0 org.projectlombok lombok ${lombok.version} org.springframework.boot spring-boot-configuration-processor ${spring-boot.version} org.apache.maven.plugins maven-surefire-plugin 2.22.2 -Dfile.encoding=UTF-8 ${profiles.active} exclude src/main/resources false src/main/resources application* bootstrap* banner* true public aliyun nexus https://maven.aliyun.com/repository/public/ true public aliyun nexus https://maven.aliyun.com/repository/public/ true false ================================================ FILE: ruoyi-admin/Dockerfile ================================================ FROM anapsix/alpine-java:8_server-jre_unlimited MAINTAINER Lion Li RUN mkdir -p /ruoyi/server/logs \ /ruoyi/server/temp \ /ruoyi/skywalking/agent WORKDIR /ruoyi/server ENV SERVER_PORT=8080 EXPOSE ${SERVER_PORT} ADD ./target/ruoyi-admin.jar ./app.jar ENTRYPOINT ["java", \ "-Djava.security.egd=file:/dev/./urandom", \ "-Dserver.port=${SERVER_PORT}", \ # 应用名称 如果想区分集群节点监控 改成不同的名称即可 # "-Dskywalking.agent.service_name=ruoyi-server", \ # "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \ "-jar", "app.jar"] ================================================ FILE: ruoyi-admin/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 jar pzc web服务入口 com.googlecode.aviator aviator 5.2.6 org.springframework.boot spring-boot-devtools true com.mysql mysql-connector-j com.oracle.database.jdbc ojdbc8 org.postgresql postgresql com.microsoft.sqlserver mssql-jdbc com.ruoyi ruoyi-framework com.ruoyi ruoyi-system com.ruoyi ruoyi-job com.ruoyi ruoyi-oss com.ruoyi ruoyi-generator com.ruoyi pai-zhi-cheng org.springframework.boot spring-boot-starter-test test junit junit test ${project.artifactId} org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} true repackage org.apache.maven.plugins maven-war-plugin 3.2.2 false ${project.artifactId} ================================================ FILE: ruoyi-admin/src/main/java/top/flya/RuoYiApplication.java ================================================ package top.flya; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; /** * 启动程序 * * @author ruoyi */ @SpringBootApplication public class RuoYiApplication { public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "true"); SpringApplication application = new SpringApplication(RuoYiApplication.class); application.setApplicationStartup(new BufferingApplicationStartup(2048)); application.run(args); System.out.println("Hello,World!"); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/RuoYiServletInitializer.java ================================================ package top.flya; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; /** * web容器中进行部署 * * @author ruoyi */ public class RuoYiServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(RuoYiApplication.class); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/common/CaptchaController.java ================================================ package top.flya.web.controller.common; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; import top.flya.common.constant.CacheConstants; import top.flya.common.constant.Constants; import top.flya.common.core.domain.R; import top.flya.common.enums.CaptchaType; import top.flya.common.utils.StringUtils; import top.flya.common.utils.email.MailUtils; import top.flya.common.utils.redis.RedisUtils; import top.flya.common.utils.reflect.ReflectUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.framework.config.properties.CaptchaProperties; import top.flya.framework.config.properties.MailProperties; import top.flya.sms.config.properties.SmsProperties; import top.flya.sms.core.SmsTemplate; import top.flya.sms.entity.SmsResult; import top.flya.system.service.ISysConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.constraints.NotBlank; import java.time.Duration; import java.util.HashMap; import java.util.Map; /** * 验证码操作处理 * * @author Lion Li */ @SaIgnore @Slf4j @Validated @RequiredArgsConstructor @RestController public class CaptchaController { private final CaptchaProperties captchaProperties; private final SmsProperties smsProperties; private final ISysConfigService configService; private final MailProperties mailProperties; /** * 短信验证码 * * @param phonenumber 用户手机号 */ @GetMapping("/captchaSms") public R smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { if (!smsProperties.getEnabled()) { return R.fail("当前系统没有开启短信功能!"); } String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber; String code = RandomUtil.randomNumbers(4); RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); // 验证码模板id 自行处理 (查数据库或写死均可) String templateId = ""; Map map = new HashMap<>(1); map.put("code", code); SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); SmsResult result = smsTemplate.send(phonenumber, templateId, map); if (!result.isSuccess()) { log.error("验证码短信发送异常 => {}", result); return R.fail(result.getMessage()); } return R.ok(); } /** * 邮箱验证码 * * @param email 邮箱 */ @GetMapping("/captchaEmail") public R emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { if (!mailProperties.getEnabled()) { return R.fail("当前系统没有开启邮箱功能!"); } String key = CacheConstants.CAPTCHA_CODE_KEY + email; String code = RandomUtil.randomNumbers(4); RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); try { MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); } catch (Exception e) { log.error("验证码短信发送异常 => {}", e.getMessage()); return R.fail(e.getMessage()); } return R.ok(); } /** * 生成验证码 */ @GetMapping("/captchaImage") public R> getCode() { Map ajax = new HashMap<>(); boolean captchaEnabled = configService.selectCaptchaEnabled(); ajax.put("captchaEnabled", captchaEnabled); if (!captchaEnabled) { return R.ok(ajax); } // 保存验证码信息 String uuid = IdUtil.simpleUUID(); String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; // 生成验证码 CaptchaType captchaType = captchaProperties.getType(); boolean isMath = CaptchaType.MATH == captchaType; Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength(); CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length); AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); captcha.setGenerator(codeGenerator); captcha.createCode(); String code = captcha.getCode(); if (isMath) { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); code = exp.getValue(String.class); } RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); ajax.put("uuid", uuid); ajax.put("img", captcha.getImageBase64()); return R.ok(ajax); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/monitor/CacheController.java ================================================ package top.flya.web.controller.monitor; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.collection.CollUtil; import top.flya.common.constant.CacheConstants; import top.flya.common.constant.CacheNames; import top.flya.common.core.domain.R; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.CacheUtils; import top.flya.common.utils.redis.RedisUtils; import top.flya.system.domain.SysCache; import lombok.RequiredArgsConstructor; import org.redisson.spring.data.connection.RedissonConnectionFactory; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.web.bind.annotation.*; import java.util.*; import java.util.stream.Collectors; /** * 缓存监控 * * @author Lion Li */ @RequiredArgsConstructor @RestController @RequestMapping("/monitor/cache") public class CacheController { private final RedissonConnectionFactory connectionFactory; private final static List CACHES = new ArrayList<>(); static { CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户")); CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息")); CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典")); 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(CacheNames.SYS_OSS_CONFIG, "OSS配置")); CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); } /** * 获取缓存监控列表 */ @SaCheckPermission("monitor:cache:list") @GetMapping() public R> getInfo() throws Exception { RedisConnection connection = connectionFactory.getConnection(); Properties info = connection.info(); Properties commandStats = connection.info("commandstats"); Long dbSize = connection.dbSize(); Map result = new HashMap<>(3); result.put("info", info); result.put("dbSize", dbSize); List> pieList = new ArrayList<>(); if (commandStats != null) { commandStats.stringPropertyNames().forEach(key -> { Map 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 R.ok(result); } /** * 获取缓存监控缓存名列表 */ @SaCheckPermission("monitor:cache:list") @GetMapping("/getNames") public R> cache() { return R.ok(CACHES); } /** * 获取缓存监控Key列表 * * @param cacheName 缓存名 */ @SaCheckPermission("monitor:cache:list") @GetMapping("/getKeys/{cacheName}") public R> getCacheKeys(@PathVariable String cacheName) { Collection cacheKeys = new HashSet<>(0); if (isCacheNames(cacheName)) { Set keys = CacheUtils.keys(cacheName); if (CollUtil.isNotEmpty(keys)) { cacheKeys = keys.stream().map(Object::toString).collect(Collectors.toList()); } } else { cacheKeys = RedisUtils.keys(cacheName + "*"); } return R.ok(cacheKeys); } /** * 获取缓存监控缓存值详情 * * @param cacheName 缓存名 * @param cacheKey 缓存key */ @SaCheckPermission("monitor:cache:list") @GetMapping("/getValue/{cacheName}/{cacheKey}") public R getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) { Object cacheValue; if (isCacheNames(cacheName)) { cacheValue = CacheUtils.get(cacheName, cacheKey); } else { cacheValue = RedisUtils.getCacheObject(cacheKey); } SysCache sysCache = new SysCache(cacheName, cacheKey, JsonUtils.toJsonString(cacheValue)); return R.ok(sysCache); } /** * 清理缓存监控缓存名 * * @param cacheName 缓存名 */ @SaCheckPermission("monitor:cache:list") @DeleteMapping("/clearCacheName/{cacheName}") public R clearCacheName(@PathVariable String cacheName) { if (isCacheNames(cacheName)) { CacheUtils.clear(cacheName); } else { RedisUtils.deleteKeys(cacheName + "*"); } return R.ok(); } /** * 清理缓存监控Key * * @param cacheKey key名 */ @SaCheckPermission("monitor:cache:list") @DeleteMapping("/clearCacheKey/{cacheName}/{cacheKey}") public R clearCacheKey(@PathVariable String cacheName, @PathVariable String cacheKey) { if (isCacheNames(cacheName)) { CacheUtils.evict(cacheName, cacheKey); } else { RedisUtils.deleteObject(cacheKey); } return R.ok(); } /** * 清理全部缓存监控 */ @SaCheckPermission("monitor:cache:list") @DeleteMapping("/clearCacheAll") public R clearCacheAll() { RedisUtils.deleteKeys("*"); return R.ok(); } private boolean isCacheNames(String cacheName) { return !StringUtils.contains(cacheName, ":"); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/monitor/SysLogininforController.java ================================================ package top.flya.web.controller.monitor; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.constant.CacheConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.common.utils.redis.RedisUtils; import top.flya.system.domain.SysLogininfor; import top.flya.system.service.ISysLogininforService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * 系统访问记录 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/monitor/logininfor") public class SysLogininforController extends BaseController { private final ISysLogininforService logininforService; /** * 获取系统访问记录列表 */ @SaCheckPermission("monitor:logininfor:list") @GetMapping("/list") public TableDataInfo list(SysLogininfor logininfor, PageQuery pageQuery) { return logininforService.selectPageLogininforList(logininfor, pageQuery); } /** * 导出系统访问记录列表 */ @Log(title = "登录日志", businessType = BusinessType.EXPORT) @SaCheckPermission("monitor:logininfor:export") @PostMapping("/export") public void export(SysLogininfor logininfor, HttpServletResponse response) { List list = logininforService.selectLogininforList(logininfor); ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response); } /** * 批量删除登录日志 * @param infoIds 日志ids */ @SaCheckPermission("monitor:logininfor:remove") @Log(title = "登录日志", businessType = BusinessType.DELETE) @DeleteMapping("/{infoIds}") public R remove(@PathVariable Long[] infoIds) { return toAjax(logininforService.deleteLogininforByIds(infoIds)); } /** * 清理系统访问记录 */ @SaCheckPermission("monitor:logininfor:remove") @Log(title = "登录日志", businessType = BusinessType.CLEAN) @DeleteMapping("/clean") public R clean() { logininforService.cleanLogininfor(); return R.ok(); } @SaCheckPermission("monitor:logininfor:unlock") @Log(title = "账户解锁", businessType = BusinessType.OTHER) @GetMapping("/unlock/{userName}") public R unlock(@PathVariable("userName") String userName) { String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName; if (RedisUtils.hasKey(loginName)) { RedisUtils.deleteObject(loginName); } return R.ok(); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/monitor/SysOperlogController.java ================================================ package top.flya.web.controller.monitor; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.SysOperLog; import top.flya.system.service.ISysOperLogService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * 操作日志记录 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/monitor/operlog") public class SysOperlogController extends BaseController { private final ISysOperLogService operLogService; /** * 获取操作日志记录列表 */ @SaCheckPermission("monitor:operlog:list") @GetMapping("/list") public TableDataInfo list(SysOperLog operLog, PageQuery pageQuery) { return operLogService.selectPageOperLogList(operLog, pageQuery); } /** * 导出操作日志记录列表 */ @Log(title = "操作日志", businessType = BusinessType.EXPORT) @SaCheckPermission("monitor:operlog:export") @PostMapping("/export") public void export(SysOperLog operLog, HttpServletResponse response) { List list = operLogService.selectOperLogList(operLog); ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response); } /** * 批量删除操作日志记录 * @param operIds 日志ids */ @Log(title = "操作日志", businessType = BusinessType.DELETE) @SaCheckPermission("monitor:operlog:remove") @DeleteMapping("/{operIds}") public R remove(@PathVariable Long[] operIds) { return toAjax(operLogService.deleteOperLogByIds(operIds)); } /** * 清理操作日志记录 */ @Log(title = "操作日志", businessType = BusinessType.CLEAN) @SaCheckPermission("monitor:operlog:remove") @DeleteMapping("/clean") public R clean() { operLogService.cleanOperLog(); return R.ok(); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/monitor/SysUserOnlineController.java ================================================ package top.flya.web.controller.monitor; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import top.flya.common.annotation.Log; import top.flya.common.constant.CacheConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.R; import top.flya.common.core.domain.dto.UserOnlineDTO; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.RedisUtils; import top.flya.system.domain.SysUserOnline; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 在线用户监控 * * @author Lion Li */ @RequiredArgsConstructor @RestController @RequestMapping("/monitor/online") public class SysUserOnlineController extends BaseController { /** * 获取在线用户监控列表 * * @param ipaddr IP地址 * @param userName 用户名 */ @SaCheckPermission("monitor:online:list") @GetMapping("/list") public TableDataInfo list(String ipaddr, String userName) { // 获取所有未过期的 token List keys = StpUtil.searchTokenValue("", 0, -1, false); List userOnlineDTOList = new ArrayList<>(); for (String key : keys) { String token = StringUtils.substringAfterLast(key, ":"); // 如果已经过期则跳过 if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) { continue; } userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)); } if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> StringUtils.equals(ipaddr, userOnline.getIpaddr()) && StringUtils.equals(userName, userOnline.getUserName()) ); } else if (StringUtils.isNotEmpty(ipaddr)) { userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> StringUtils.equals(ipaddr, userOnline.getIpaddr()) ); } else if (StringUtils.isNotEmpty(userName)) { userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> StringUtils.equals(userName, userOnline.getUserName()) ); } Collections.reverse(userOnlineDTOList); userOnlineDTOList.removeAll(Collections.singleton(null)); List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); return TableDataInfo.build(userOnlineList); } /** * 强退用户 * * @param tokenId token值 */ @SaCheckPermission("monitor:online:forceLogout") @Log(title = "在线用户", businessType = BusinessType.FORCE) @DeleteMapping("/{tokenId}") public R forceLogout(@PathVariable String tokenId) { try { StpUtil.kickoutByTokenValue(tokenId); } catch (NotLoginException ignored) { } return R.ok(); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysConfigController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.constant.UserConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.SysConfig; import top.flya.system.service.ISysConfigService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * 参数配置 信息操作处理 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/config") public class SysConfigController extends BaseController { private final ISysConfigService configService; /** * 获取参数配置列表 */ @SaCheckPermission("system:config:list") @GetMapping("/list") public TableDataInfo list(SysConfig config, PageQuery pageQuery) { return configService.selectPageConfigList(config, pageQuery); } /** * 导出参数配置列表 */ @Log(title = "参数管理", businessType = BusinessType.EXPORT) @SaCheckPermission("system:config:export") @PostMapping("/export") public void export(SysConfig config, HttpServletResponse response) { List list = configService.selectConfigList(config); ExcelUtil.exportExcel(list, "参数数据", SysConfig.class, response); } /** * 根据参数编号获取详细信息 * * @param configId 参数ID */ @SaCheckPermission("system:config:query") @GetMapping(value = "/{configId}") public R getInfo(@PathVariable Long configId) { return R.ok(configService.selectConfigById(configId)); } /** * 根据参数键名查询参数值 * * @param configKey 参数Key */ @GetMapping(value = "/configKey/{configKey}") public R getConfigKey(@PathVariable String configKey) { return R.ok(configService.selectConfigByKey(configKey)); } /** * 新增参数配置 */ @SaCheckPermission("system:config:add") @Log(title = "参数管理", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysConfig config) { if (!configService.checkConfigKeyUnique(config)) { return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); } configService.insertConfig(config); return R.ok(); } /** * 修改参数配置 */ @SaCheckPermission("system:config:edit") @Log(title = "参数管理", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysConfig config) { if (!configService.checkConfigKeyUnique(config)) { return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); } configService.updateConfig(config); return R.ok(); } /** * 根据参数键名修改参数配置 */ @SaCheckPermission("system:config:edit") @Log(title = "参数管理", businessType = BusinessType.UPDATE) @PutMapping("/updateByKey") public R updateByKey(@RequestBody SysConfig config) { configService.updateConfig(config); return R.ok(); } /** * 删除参数配置 * * @param configIds 参数ID串 */ @SaCheckPermission("system:config:remove") @Log(title = "参数管理", businessType = BusinessType.DELETE) @DeleteMapping("/{configIds}") public R remove(@PathVariable Long[] configIds) { configService.deleteConfigByIds(configIds); return R.ok(); } /** * 刷新参数缓存 */ @SaCheckPermission("system:config:remove") @Log(title = "参数管理", businessType = BusinessType.CLEAN) @DeleteMapping("/refreshCache") public R refreshCache() { configService.resetConfigCache(); return R.ok(); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysDeptController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.convert.Convert; import top.flya.common.annotation.Log; import top.flya.common.constant.UserConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysDept; import top.flya.common.enums.BusinessType; import top.flya.common.utils.StringUtils; import top.flya.system.service.ISysDeptService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 部门信息 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/dept") public class SysDeptController extends BaseController { private final ISysDeptService deptService; /** * 获取部门列表 */ @SaCheckPermission("system:dept:list") @GetMapping("/list") public R> list(SysDept dept) { List depts = deptService.selectDeptList(dept); return R.ok(depts); } /** * 查询部门列表(排除节点) * * @param deptId 部门ID */ @SaCheckPermission("system:dept:list") @GetMapping("/list/exclude/{deptId}") public R> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) { List depts = deptService.selectDeptList(new SysDept()); depts.removeIf(d -> d.getDeptId().equals(deptId) || StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId))); return R.ok(depts); } /** * 根据部门编号获取详细信息 * * @param deptId 部门ID */ @SaCheckPermission("system:dept:query") @GetMapping(value = "/{deptId}") public R getInfo(@PathVariable Long deptId) { deptService.checkDeptDataScope(deptId); return R.ok(deptService.selectDeptById(deptId)); } /** * 新增部门 */ @SaCheckPermission("system:dept:add") @Log(title = "部门管理", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysDept dept) { if (!deptService.checkDeptNameUnique(dept)) { return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); } return toAjax(deptService.insertDept(dept)); } /** * 修改部门 */ @SaCheckPermission("system:dept:edit") @Log(title = "部门管理", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysDept dept) { Long deptId = dept.getDeptId(); deptService.checkDeptDataScope(deptId); if (!deptService.checkDeptNameUnique(dept)) { return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); } else if (dept.getParentId().equals(deptId)) { return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) { return R.fail("该部门包含未停用的子部门!"); } return toAjax(deptService.updateDept(dept)); } /** * 删除部门 * * @param deptId 部门ID */ @SaCheckPermission("system:dept:remove") @Log(title = "部门管理", businessType = BusinessType.DELETE) @DeleteMapping("/{deptId}") public R remove(@PathVariable Long deptId) { if (deptService.hasChildByDeptId(deptId)) { return R.warn("存在下级部门,不允许删除"); } if (deptService.checkDeptExistUser(deptId)) { return R.warn("部门存在用户,不允许删除"); } deptService.checkDeptDataScope(deptId); return toAjax(deptService.deleteDeptById(deptId)); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysDictDataController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.util.ObjectUtil; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysDictData; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.service.ISysDictDataService; import top.flya.system.service.ISysDictTypeService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; /** * 数据字典信息 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/dict/data") public class SysDictDataController extends BaseController { private final ISysDictDataService dictDataService; private final ISysDictTypeService dictTypeService; /** * 查询字典数据列表 */ // @SaCheckPermission("system:dict:list") @GetMapping("/list") public TableDataInfo list(SysDictData dictData, PageQuery pageQuery) { return dictDataService.selectPageDictDataList(dictData, pageQuery); } /** * 导出字典数据列表 */ @Log(title = "字典数据", businessType = BusinessType.EXPORT) @SaCheckPermission("system:dict:export") @PostMapping("/export") public void export(SysDictData dictData, HttpServletResponse response) { List list = dictDataService.selectDictDataList(dictData); ExcelUtil.exportExcel(list, "字典数据", SysDictData.class, response); } /** * 查询字典数据详细 * * @param dictCode 字典code */ // @SaCheckPermission("system:dict:query") @GetMapping(value = "/{dictCode}") public R getInfo(@PathVariable Long dictCode) { return R.ok(dictDataService.selectDictDataById(dictCode)); } /** * 根据字典类型查询字典数据信息 * * @param dictType 字典类型 */ @GetMapping(value = "/type/{dictType}") public R> dictType(@PathVariable String dictType) { List data = dictTypeService.selectDictDataByType(dictType); if (ObjectUtil.isNull(data)) { data = new ArrayList<>(); } return R.ok(data); } /** * 新增字典类型 */ @SaCheckPermission("system:dict:add") @Log(title = "字典数据", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysDictData dict) { dictDataService.insertDictData(dict); return R.ok(); } /** * 修改保存字典类型 */ @SaCheckPermission("system:dict:edit") @Log(title = "字典数据", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysDictData dict) { dictDataService.updateDictData(dict); return R.ok(); } /** * 删除字典类型 * * @param dictCodes 字典code串 */ @SaCheckPermission("system:dict:remove") @Log(title = "字典类型", businessType = BusinessType.DELETE) @DeleteMapping("/{dictCodes}") public R remove(@PathVariable Long[] dictCodes) { dictDataService.deleteDictDataByIds(dictCodes); return R.ok(); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysDictTypeController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.constant.UserConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysDictType; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.service.ISysDictTypeService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * 数据字典信息 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/dict/type") public class SysDictTypeController extends BaseController { private final ISysDictTypeService dictTypeService; /** * 查询字典类型列表 */ @SaCheckPermission("system:dict:list") @GetMapping("/list") public TableDataInfo list(SysDictType dictType, PageQuery pageQuery) { return dictTypeService.selectPageDictTypeList(dictType, pageQuery); } /** * 导出字典类型列表 */ @Log(title = "字典类型", businessType = BusinessType.EXPORT) @SaCheckPermission("system:dict:export") @PostMapping("/export") public void export(SysDictType dictType, HttpServletResponse response) { List list = dictTypeService.selectDictTypeList(dictType); ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response); } /** * 查询字典类型详细 * * @param dictId 字典ID */ @SaCheckPermission("system:dict:query") @GetMapping(value = "/{dictId}") public R getInfo(@PathVariable Long dictId) { return R.ok(dictTypeService.selectDictTypeById(dictId)); } /** * 新增字典类型 */ @SaCheckPermission("system:dict:add") @Log(title = "字典类型", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysDictType dict) { if (!dictTypeService.checkDictTypeUnique(dict)) { return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); } dictTypeService.insertDictType(dict); return R.ok(); } /** * 修改字典类型 */ @SaCheckPermission("system:dict:edit") @Log(title = "字典类型", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysDictType dict) { if (!dictTypeService.checkDictTypeUnique(dict)) { return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); } dictTypeService.updateDictType(dict); return R.ok(); } /** * 删除字典类型 * * @param dictIds 字典ID串 */ @SaCheckPermission("system:dict:remove") @Log(title = "字典类型", businessType = BusinessType.DELETE) @DeleteMapping("/{dictIds}") public R remove(@PathVariable Long[] dictIds) { dictTypeService.deleteDictTypeByIds(dictIds); return R.ok(); } /** * 刷新字典缓存 */ @SaCheckPermission("system:dict:remove") @Log(title = "字典类型", businessType = BusinessType.CLEAN) @DeleteMapping("/refreshCache") public R refreshCache() { dictTypeService.resetDictCache(); return R.ok(); } /** * 获取字典选择框列表 */ @GetMapping("/optionselect") public R> optionselect() { List dictTypes = dictTypeService.selectDictTypeAll(); return R.ok(dictTypes); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysIndexController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaIgnore; import top.flya.common.config.RuoYiConfig; import top.flya.common.utils.StringUtils; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * 首页 * * @author Lion Li */ @RequiredArgsConstructor @RestController public class SysIndexController { /** * 系统基础配置 */ private final RuoYiConfig ruoyiConfig; /** * 访问首页,提示语 */ @SaIgnore @GetMapping("/") public String index() { return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysLoginController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaIgnore; import top.flya.common.constant.Constants; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysMenu; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.domain.model.EmailLoginBody; import top.flya.common.core.domain.model.LoginBody; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.core.domain.model.SmsLoginBody; import top.flya.common.helper.LoginHelper; import top.flya.system.domain.bo.PzcUserBo; import top.flya.system.domain.vo.RouterVo; import top.flya.system.service.ISysMenuService; import top.flya.system.service.ISysUserService; import top.flya.system.service.SysLoginService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import javax.validation.constraints.NotBlank; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 登录验证 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController public class SysLoginController { private final SysLoginService loginService; private final ISysMenuService menuService; private final ISysUserService userService; /** * 登录方法 * * @param loginBody 登录信息 * @return 结果 */ @SaIgnore @PostMapping("/login") public R> login(@Validated @RequestBody LoginBody loginBody) { Map ajax = new HashMap<>(); // 生成令牌 String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid()); ajax.put(Constants.TOKEN, token); return R.ok(ajax); } /** * 短信登录 * * @param smsLoginBody 登录信息 * @return 结果 */ @SaIgnore @PostMapping("/smsLogin") public R> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) { Map ajax = new HashMap<>(); // 生成令牌 String token = loginService.smsLogin(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode()); ajax.put(Constants.TOKEN, token); return R.ok(ajax); } /** * 邮件登录 * * @param body 登录信息 * @return 结果 */ @PostMapping("/emailLogin") public R> emailLogin(@Validated @RequestBody EmailLoginBody body) { Map ajax = new HashMap<>(); // 生成令牌 String token = loginService.emailLogin(body.getEmail(), body.getEmailCode()); ajax.put(Constants.TOKEN, token); return R.ok(ajax); } /** * @return 结果 */ @SaIgnore @PostMapping("/xcxLogin") public R> xcxLogin(@RequestBody PzcUserBo pzcUserBo) { Map ajax = new HashMap<>(); // 生成令牌 String token = loginService.xcxLogin(pzcUserBo.getLoginCode()); ajax.put(Constants.TOKEN, token); return R.ok(ajax); } /** * 退出登录 */ @SaIgnore @PostMapping("/logout") public R logout() { loginService.logout(); return R.ok("退出成功"); } /** * 获取用户信息 * * @return 用户信息 */ @GetMapping("getInfo") public R> getInfo() { LoginUser loginUser = LoginHelper.getLoginUser(); SysUser user = userService.selectUserById(loginUser.getUserId()); Map ajax = new HashMap<>(); ajax.put("user", user); ajax.put("roles", loginUser.getRolePermission()); ajax.put("permissions", loginUser.getMenuPermission()); return R.ok(ajax); } /** * 获取路由信息 * * @return 路由信息 */ @GetMapping("getRouters") public R> getRouters() { Long userId = LoginHelper.getUserId(); List menus = menuService.selectMenuTreeByUserId(userId); return R.ok(menuService.buildMenus(menus)); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysMenuController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.lang.tree.Tree; import top.flya.common.annotation.Log; import top.flya.common.constant.UserConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysMenu; import top.flya.common.enums.BusinessType; import top.flya.common.utils.StringUtils; import top.flya.system.service.ISysMenuService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 菜单信息 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/menu") public class SysMenuController extends BaseController { private final ISysMenuService menuService; /** * 获取菜单列表 */ @SaCheckPermission("system:menu:list") @GetMapping("/list") public R> list(SysMenu menu) { List menus = menuService.selectMenuList(menu, getUserId()); return R.ok(menus); } /** * 根据菜单编号获取详细信息 * * @param menuId 菜单ID */ @SaCheckPermission("system:menu:query") @GetMapping(value = "/{menuId}") public R getInfo(@PathVariable Long menuId) { return R.ok(menuService.selectMenuById(menuId)); } /** * 获取菜单下拉树列表 */ @GetMapping("/treeselect") public R>> treeselect(SysMenu menu) { List menus = menuService.selectMenuList(menu, getUserId()); return R.ok(menuService.buildMenuTreeSelect(menus)); } /** * 加载对应角色菜单列表树 * * @param roleId 角色ID */ @GetMapping(value = "/roleMenuTreeselect/{roleId}") public R> roleMenuTreeselect(@PathVariable("roleId") Long roleId) { List menus = menuService.selectMenuList(getUserId()); Map ajax = new HashMap<>(); ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); ajax.put("menus", menuService.buildMenuTreeSelect(menus)); return R.ok(ajax); } /** * 新增菜单 */ @SaCheckPermission("system:menu:add") @Log(title = "菜单管理", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysMenu menu) { if (!menuService.checkMenuNameUnique(menu)) { return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); } return toAjax(menuService.insertMenu(menu)); } /** * 修改菜单 */ @SaCheckPermission("system:menu:edit") @Log(title = "菜单管理", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysMenu menu) { if (!menuService.checkMenuNameUnique(menu)) { return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); } else if (menu.getMenuId().equals(menu.getParentId())) { return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); } return toAjax(menuService.updateMenu(menu)); } /** * 删除菜单 * * @param menuId 菜单ID */ @SaCheckPermission("system:menu:remove") @Log(title = "菜单管理", businessType = BusinessType.DELETE) @DeleteMapping("/{menuId}") public R remove(@PathVariable("menuId") Long menuId) { if (menuService.hasChildByMenuId(menuId)) { return R.warn("存在子菜单,不允许删除"); } if (menuService.checkMenuExistRole(menuId)) { return R.warn("菜单已分配,不允许删除"); } return toAjax(menuService.deleteMenuById(menuId)); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysNoticeController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.system.domain.SysNotice; import top.flya.system.service.ISysNoticeService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; /** * 公告 信息操作处理 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/notice") public class SysNoticeController extends BaseController { private final ISysNoticeService noticeService; /** * 获取通知公告列表 */ @SaCheckPermission("system:notice:list") @GetMapping("/list") public TableDataInfo list(SysNotice notice, PageQuery pageQuery) { return noticeService.selectPageNoticeList(notice, pageQuery); } /** * 根据通知公告编号获取详细信息 * * @param noticeId 公告ID */ @SaCheckPermission("system:notice:query") @GetMapping(value = "/{noticeId}") public R getInfo(@PathVariable Long noticeId) { return R.ok(noticeService.selectNoticeById(noticeId)); } /** * 新增通知公告 */ @SaCheckPermission("system:notice:add") @Log(title = "通知公告", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysNotice notice) { return toAjax(noticeService.insertNotice(notice)); } /** * 修改通知公告 */ @SaCheckPermission("system:notice:edit") @Log(title = "通知公告", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysNotice notice) { return toAjax(noticeService.updateNotice(notice)); } /** * 删除通知公告 * * @param noticeIds 公告ID串 */ @SaCheckPermission("system:notice:remove") @Log(title = "通知公告", businessType = BusinessType.DELETE) @DeleteMapping("/{noticeIds}") public R remove(@PathVariable Long[] noticeIds) { return toAjax(noticeService.deleteNoticeByIds(noticeIds)); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysOssConfigController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.core.validate.QueryGroup; import top.flya.common.enums.BusinessType; import top.flya.system.domain.bo.SysOssConfigBo; import top.flya.system.domain.vo.SysOssConfigVo; import top.flya.system.service.ISysOssConfigService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; /** * 对象存储配置 * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/oss/config") public class SysOssConfigController extends BaseController { private final ISysOssConfigService iSysOssConfigService; /** * 查询对象存储配置列表 */ @SaCheckPermission("system:oss:list") @GetMapping("/list") public TableDataInfo list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) { return iSysOssConfigService.queryPageList(bo, pageQuery); } /** * 获取对象存储配置详细信息 * * @param ossConfigId OSS配置ID */ @SaCheckPermission("system:oss:query") @GetMapping("/{ossConfigId}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long ossConfigId) { return R.ok(iSysOssConfigService.queryById(ossConfigId)); } /** * 新增对象存储配置 */ @SaCheckPermission("system:oss:add") @Log(title = "对象存储配置", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) { return toAjax(iSysOssConfigService.insertByBo(bo)); } /** * 修改对象存储配置 */ @SaCheckPermission("system:oss:edit") @Log(title = "对象存储配置", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) { return toAjax(iSysOssConfigService.updateByBo(bo)); } /** * 删除对象存储配置 * * @param ossConfigIds OSS配置ID串 */ @SaCheckPermission("system:oss:remove") @Log(title = "对象存储配置", businessType = BusinessType.DELETE) @DeleteMapping("/{ossConfigIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ossConfigIds) { return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true)); } /** * 状态修改 */ @SaCheckPermission("system:oss:edit") @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE) @PutMapping("/changeStatus") public R changeStatus(@RequestBody SysOssConfigBo bo) { return toAjax(iSysOssConfigService.updateOssConfigStatus(bo)); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysOssController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.util.ObjectUtil; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.validate.QueryGroup; import top.flya.common.enums.BusinessType; import top.flya.system.domain.bo.SysOssBo; import top.flya.system.domain.vo.SysOssVo; import top.flya.system.service.ISysOssService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 文件上传 控制层 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/oss") public class SysOssController extends BaseController { private final ISysOssService iSysOssService; /** * 查询OSS对象存储列表 */ @SaCheckPermission("system:oss:list") @GetMapping("/list") public TableDataInfo list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) { return iSysOssService.queryPageList(bo, pageQuery); } /** * 查询OSS对象基于id串 * * @param ossIds OSS对象ID串 */ @SaCheckPermission("system:oss:list") @GetMapping("/listByIds/{ossIds}") public R> listByIds(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ossIds) { List list = iSysOssService.listByIds(Arrays.asList(ossIds)); return R.ok(list); } /** * 上传OSS对象存储 * * @param file 文件 */ // @SaCheckPermission("system:oss:upload") @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R> upload(@RequestPart("file") MultipartFile file) { if (ObjectUtil.isNull(file)) { return R.fail("上传文件不能为空"); } SysOssVo oss = iSysOssService.upload(file); Map map = new HashMap<>(2); map.put("url", oss.getUrl()); map.put("fileName", oss.getOriginalName()); map.put("ossId", oss.getOssId().toString()); return R.ok(map); } /** * 下载OSS对象 * * @param ossId OSS对象ID */ @SaCheckPermission("system:oss:download") @GetMapping("/download/{ossId}") public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException { iSysOssService.download(ossId,response); } /** * 删除OSS对象存储 * * @param ossIds OSS对象ID串 */ @SaCheckPermission("system:oss:remove") @Log(title = "OSS对象存储", businessType = BusinessType.DELETE) @DeleteMapping("/{ossIds}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ossIds) { return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true)); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysPostController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.constant.UserConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.SysPost; import top.flya.system.service.ISysPostService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * 岗位信息操作处理 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/post") public class SysPostController extends BaseController { private final ISysPostService postService; /** * 获取岗位列表 */ @SaCheckPermission("system:post:list") @GetMapping("/list") public TableDataInfo list(SysPost post, PageQuery pageQuery) { return postService.selectPagePostList(post, pageQuery); } /** * 导出岗位列表 */ @Log(title = "岗位管理", businessType = BusinessType.EXPORT) @SaCheckPermission("system:post:export") @PostMapping("/export") public void export(SysPost post, HttpServletResponse response) { List list = postService.selectPostList(post); ExcelUtil.exportExcel(list, "岗位数据", SysPost.class, response); } /** * 根据岗位编号获取详细信息 * * @param postId 岗位ID */ @SaCheckPermission("system:post:query") @GetMapping(value = "/{postId}") public R getInfo(@PathVariable Long postId) { return R.ok(postService.selectPostById(postId)); } /** * 新增岗位 */ @SaCheckPermission("system:post:add") @Log(title = "岗位管理", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysPost post) { if (!postService.checkPostNameUnique(post)) { return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); } else if (!postService.checkPostCodeUnique(post)) { return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); } return toAjax(postService.insertPost(post)); } /** * 修改岗位 */ @SaCheckPermission("system:post:edit") @Log(title = "岗位管理", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysPost post) { if (!postService.checkPostNameUnique(post)) { return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); } else if (!postService.checkPostCodeUnique(post)) { return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); } return toAjax(postService.updatePost(post)); } /** * 删除岗位 * * @param postIds 岗位ID串 */ @SaCheckPermission("system:post:remove") @Log(title = "岗位管理", businessType = BusinessType.DELETE) @DeleteMapping("/{postIds}") public R remove(@PathVariable Long[] postIds) { return toAjax(postService.deletePostByIds(postIds)); } /** * 获取岗位选择框列表 */ @GetMapping("/optionselect") public R> optionselect() { List posts = postService.selectPostAll(); return R.ok(posts); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysProfileController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.io.FileUtil; import top.flya.common.annotation.Log; import top.flya.common.constant.UserConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.enums.BusinessType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StringUtils; import top.flya.common.utils.file.MimeTypeUtils; import top.flya.system.domain.SysOss; import top.flya.system.domain.vo.SysOssVo; import top.flya.system.service.ISysOssService; import top.flya.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * 个人信息 业务处理 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/user/profile") public class SysProfileController extends BaseController { private final ISysUserService userService; private final ISysOssService iSysOssService; /** * 个人信息 */ @GetMapping public R> profile() { SysUser user = userService.selectUserById(getUserId()); Map ajax = new HashMap<>(); ajax.put("user", user); ajax.put("roleGroup", userService.selectUserRoleGroup(user.getUserName())); ajax.put("postGroup", userService.selectUserPostGroup(user.getUserName())); return R.ok(ajax); } /** * 修改用户 */ @Log(title = "个人信息", businessType = BusinessType.UPDATE) @PutMapping public R updateProfile(@RequestBody SysUser user) { if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); } if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); } user.setUserId(getUserId()); user.setUserName(null); user.setPassword(null); user.setAvatar(null); user.setDeptId(null); if (userService.updateUserProfile(user) > 0) { return R.ok(); } return R.fail("修改个人信息异常,请联系管理员"); } /** * 重置密码 * * @param newPassword 旧密码 * @param oldPassword 新密码 */ @Log(title = "个人信息", businessType = BusinessType.UPDATE) @PutMapping("/updatePwd") public R updatePwd(String oldPassword, String newPassword) { SysUser user = userService.selectUserById(LoginHelper.getUserId()); String userName = user.getUserName(); String password = user.getPassword(); if (!BCrypt.checkpw(oldPassword, password)) { return R.fail("修改密码失败,旧密码错误"); } if (BCrypt.checkpw(newPassword, password)) { return R.fail("新密码不能与旧密码相同"); } if (userService.resetUserPwd(userName, BCrypt.hashpw(newPassword)) > 0) { return R.ok(); } return R.fail("修改密码异常,请联系管理员"); } /** * 头像上传 * * @param avatarfile 用户头像 */ @Log(title = "用户头像", businessType = BusinessType.UPDATE) @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) { Map ajax = new HashMap<>(); if (!avatarfile.isEmpty()) { String extension = FileUtil.extName(avatarfile.getOriginalFilename()); if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); } SysOssVo oss = iSysOssService.upload(avatarfile); String avatar = oss.getUrl(); if (userService.updateUserAvatar(getUsername(), avatar)) { ajax.put("imgUrl", avatar); return R.ok(ajax); } } return R.fail("上传图片异常,请联系管理员"); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysRegisterController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaIgnore; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.R; import top.flya.common.core.domain.model.RegisterBody; import top.flya.system.service.ISysConfigService; import top.flya.system.service.SysRegisterService; import lombok.RequiredArgsConstructor; 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.RestController; /** * 注册验证 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController public class SysRegisterController extends BaseController { private final SysRegisterService registerService; private final ISysConfigService configService; /** * 用户注册 */ @SaIgnore @PostMapping("/register") public R register(@Validated @RequestBody RegisterBody user) { if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) { return R.fail("当前系统没有开启注册功能!"); } registerService.register(user); return R.ok(); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysRoleController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysDept; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.SysUserRole; import top.flya.system.service.ISysDeptService; import top.flya.system.service.ISysRoleService; import top.flya.system.service.ISysUserService; import top.flya.system.service.SysPermissionService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 角色信息 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/role") public class SysRoleController extends BaseController { private final ISysRoleService roleService; private final ISysUserService userService; private final ISysDeptService deptService; private final SysPermissionService permissionService; /** * 获取角色信息列表 */ @SaCheckPermission("system:role:list") @GetMapping("/list") public TableDataInfo list(SysRole role, PageQuery pageQuery) { return roleService.selectPageRoleList(role, pageQuery); } /** * 导出角色信息列表 */ @Log(title = "角色管理", businessType = BusinessType.EXPORT) @SaCheckPermission("system:role:export") @PostMapping("/export") public void export(SysRole role, HttpServletResponse response) { List list = roleService.selectRoleList(role); ExcelUtil.exportExcel(list, "角色数据", SysRole.class, response); } /** * 根据角色编号获取详细信息 * * @param roleId 角色ID */ @SaCheckPermission("system:role:query") @GetMapping(value = "/{roleId}") public R getInfo(@PathVariable Long roleId) { roleService.checkRoleDataScope(roleId); return R.ok(roleService.selectRoleById(roleId)); } /** * 新增角色 */ @SaCheckPermission("system:role:add") @Log(title = "角色管理", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysRole role) { if (!roleService.checkRoleNameUnique(role)) { return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); } else if (!roleService.checkRoleKeyUnique(role)) { return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); } return toAjax(roleService.insertRole(role)); } /** * 修改保存角色 */ @SaCheckPermission("system:role:edit") @Log(title = "角色管理", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysRole role) { roleService.checkRoleAllowed(role); roleService.checkRoleDataScope(role.getRoleId()); if (!roleService.checkRoleNameUnique(role)) { return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); } else if (!roleService.checkRoleKeyUnique(role)) { return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); } if (roleService.updateRole(role) > 0) { roleService.cleanOnlineUserByRole(role.getRoleId()); return R.ok(); } return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); } /** * 修改保存数据权限 */ @SaCheckPermission("system:role:edit") @Log(title = "角色管理", businessType = BusinessType.UPDATE) @PutMapping("/dataScope") public R dataScope(@RequestBody SysRole role) { roleService.checkRoleAllowed(role); roleService.checkRoleDataScope(role.getRoleId()); return toAjax(roleService.authDataScope(role)); } /** * 状态修改 */ @SaCheckPermission("system:role:edit") @Log(title = "角色管理", businessType = BusinessType.UPDATE) @PutMapping("/changeStatus") public R changeStatus(@RequestBody SysRole role) { roleService.checkRoleAllowed(role); roleService.checkRoleDataScope(role.getRoleId()); return toAjax(roleService.updateRoleStatus(role)); } /** * 删除角色 * * @param roleIds 角色ID串 */ @SaCheckPermission("system:role:remove") @Log(title = "角色管理", businessType = BusinessType.DELETE) @DeleteMapping("/{roleIds}") public R remove(@PathVariable Long[] roleIds) { return toAjax(roleService.deleteRoleByIds(roleIds)); } /** * 获取角色选择框列表 */ @SaCheckPermission("system:role:query") @GetMapping("/optionselect") public R> optionselect() { return R.ok(roleService.selectRoleAll()); } /** * 查询已分配用户角色列表 */ @SaCheckPermission("system:role:list") @GetMapping("/authUser/allocatedList") public TableDataInfo allocatedList(SysUser user, PageQuery pageQuery) { return userService.selectAllocatedList(user, pageQuery); } /** * 查询未分配用户角色列表 */ @SaCheckPermission("system:role:list") @GetMapping("/authUser/unallocatedList") public TableDataInfo unallocatedList(SysUser user, PageQuery pageQuery) { return userService.selectUnallocatedList(user, pageQuery); } /** * 取消授权用户 */ @SaCheckPermission("system:role:edit") @Log(title = "角色管理", businessType = BusinessType.GRANT) @PutMapping("/authUser/cancel") public R cancelAuthUser(@RequestBody SysUserRole userRole) { return toAjax(roleService.deleteAuthUser(userRole)); } /** * 批量取消授权用户 * * @param roleId 角色ID * @param userIds 用户ID串 */ @SaCheckPermission("system:role:edit") @Log(title = "角色管理", businessType = BusinessType.GRANT) @PutMapping("/authUser/cancelAll") public R cancelAuthUserAll(Long roleId, Long[] userIds) { return toAjax(roleService.deleteAuthUsers(roleId, userIds)); } /** * 批量选择用户授权 * * @param roleId 角色ID * @param userIds 用户ID串 */ @SaCheckPermission("system:role:edit") @Log(title = "角色管理", businessType = BusinessType.GRANT) @PutMapping("/authUser/selectAll") public R selectAuthUserAll(Long roleId, Long[] userIds) { roleService.checkRoleDataScope(roleId); return toAjax(roleService.insertAuthUsers(roleId, userIds)); } /** * 获取对应角色部门树列表 * * @param roleId 角色ID */ @SaCheckPermission("system:role:list") @GetMapping(value = "/deptTree/{roleId}") public R> roleDeptTreeselect(@PathVariable("roleId") Long roleId) { Map ajax = new HashMap<>(); ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); return R.ok(ajax); } } ================================================ FILE: ruoyi-admin/src/main/java/top/flya/web/controller/system/SysUserController.java ================================================ package top.flya.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import top.flya.common.annotation.Log; import top.flya.common.constant.UserConstants; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.domain.entity.SysDept; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.common.excel.ExcelResult; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.poi.ExcelUtil; import top.flya.system.domain.vo.SysUserExportVo; import top.flya.system.domain.vo.SysUserImportVo; import top.flya.system.listener.SysUserImportListener; import top.flya.system.service.ISysDeptService; import top.flya.system.service.ISysPostService; import top.flya.system.service.ISysRoleService; import top.flya.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 用户信息 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/system/user") public class SysUserController extends BaseController { private final ISysUserService userService; private final ISysRoleService roleService; private final ISysPostService postService; private final ISysDeptService deptService; /** * 获取用户列表 */ @SaCheckPermission("system:user:list") @GetMapping("/list") public TableDataInfo list(SysUser user, PageQuery pageQuery) { return userService.selectPageUserList(user, pageQuery); } /** * 导出用户列表 */ @Log(title = "用户管理", businessType = BusinessType.EXPORT) @SaCheckPermission("system:user:export") @PostMapping("/export") public void export(SysUser user, HttpServletResponse response) { List list = userService.selectUserList(user); List listVo = BeanUtil.copyToList(list, SysUserExportVo.class); for (int i = 0; i < list.size(); i++) { SysDept dept = list.get(i).getDept(); SysUserExportVo vo = listVo.get(i); if (ObjectUtil.isNotEmpty(dept)) { vo.setDeptName(dept.getDeptName()); vo.setLeader(dept.getLeader()); } } ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); } /** * 导入数据 * * @param file 导入文件 * @param updateSupport 是否更新已存在数据 */ @Log(title = "用户管理", businessType = BusinessType.IMPORT) @SaCheckPermission("system:user:import") @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { ExcelResult result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport)); return R.ok(result.getAnalysis()); } /** * 获取导入模板 */ @PostMapping("/importTemplate") public void importTemplate(HttpServletResponse response) { ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response); } /** * 根据用户编号获取详细信息 * * @param userId 用户ID */ @SaCheckPermission("system:user:query") @GetMapping(value = {"/", "/{userId}"}) public R> getInfo(@PathVariable(value = "userId", required = false) Long userId) { userService.checkUserDataScope(userId); Map ajax = new HashMap<>(); List roles = roleService.selectRoleAll(); ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin())); ajax.put("posts", postService.selectPostAll()); if (ObjectUtil.isNotNull(userId)) { SysUser sysUser = userService.selectUserById(userId); ajax.put("user", sysUser); ajax.put("postIds", postService.selectPostListByUserId(userId)); ajax.put("roleIds", StreamUtils.toList(sysUser.getRoles(), SysRole::getRoleId)); } return R.ok(ajax); } /** * 新增用户 */ @SaCheckPermission("system:user:add") @Log(title = "用户管理", businessType = BusinessType.INSERT) @PostMapping public R add(@Validated @RequestBody SysUser user) { if (!userService.checkUserNameUnique(user)) { return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); } user.setPassword(BCrypt.hashpw(user.getPassword())); return toAjax(userService.insertUser(user)); } /** * 修改用户 */ @SaCheckPermission("system:user:edit") @Log(title = "用户管理", businessType = BusinessType.UPDATE) @PutMapping public R edit(@Validated @RequestBody SysUser user) { userService.checkUserAllowed(user); userService.checkUserDataScope(user.getUserId()); if (!userService.checkUserNameUnique(user)) { return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); } return toAjax(userService.updateUser(user)); } /** * 删除用户 * * @param userIds 角色ID串 */ @SaCheckPermission("system:user:remove") @Log(title = "用户管理", businessType = BusinessType.DELETE) @DeleteMapping("/{userIds}") public R remove(@PathVariable Long[] userIds) { if (ArrayUtil.contains(userIds, getUserId())) { return R.fail("当前用户不能删除"); } return toAjax(userService.deleteUserByIds(userIds)); } /** * 重置密码 */ @SaCheckPermission("system:user:resetPwd") @Log(title = "用户管理", businessType = BusinessType.UPDATE) @PutMapping("/resetPwd") public R resetPwd(@RequestBody SysUser user) { userService.checkUserAllowed(user); userService.checkUserDataScope(user.getUserId()); user.setPassword(BCrypt.hashpw(user.getPassword())); return toAjax(userService.resetPwd(user)); } /** * 状态修改 */ @SaCheckPermission("system:user:edit") @Log(title = "用户管理", businessType = BusinessType.UPDATE) @PutMapping("/changeStatus") public R changeStatus(@RequestBody SysUser user) { userService.checkUserAllowed(user); userService.checkUserDataScope(user.getUserId()); return toAjax(userService.updateUserStatus(user)); } /** * 根据用户编号获取授权角色 * * @param userId 用户ID */ @SaCheckPermission("system:user:query") @GetMapping("/authRole/{userId}") public R> authRole(@PathVariable Long userId) { SysUser user = userService.selectUserById(userId); List roles = roleService.selectRolesByUserId(userId); Map ajax = new HashMap<>(); ajax.put("user", user); ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin())); return R.ok(ajax); } /** * 用户授权角色 * * @param userId 用户Id * @param roleIds 角色ID串 */ @SaCheckPermission("system:user:edit") @Log(title = "用户管理", businessType = BusinessType.GRANT) @PutMapping("/authRole") public R insertAuthRole(Long userId, Long[] roleIds) { userService.checkUserDataScope(userId); userService.insertUserAuth(userId, roleIds); return R.ok(); } /** * 获取部门树列表 */ @SaCheckPermission("system:user:list") @GetMapping("/deptTree") public R>> deptTree(SysDept dept) { return R.ok(deptService.selectDeptTreeList(dept)); } } ================================================ FILE: ruoyi-admin/src/main/resources/Dockerfile ================================================ FROM gozap/oraclejdk8 LABEL maintainer=fengli # /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && RUN echo 'Asia/Shanghai' >/etc/timezone COPY ./ruoyi-admin/target/*.jar /app.jar EXPOSE 8080 # hospital-manage ENTRYPOINT ["/bin/sh","-c","java -Dfile.encoding=utf8 -Djava.security.egd=file:/dev/./urandom -jar /app.jar "] ================================================ FILE: ruoyi-admin/src/main/resources/apiclient_key.pem ================================================ -----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpUtNYFC/SVijj mOsW2vVIewgnOfB2YuUW8Q0LUbLtN8jfPJrv2BziCWxVciqNtMKO2ONerRtedzar D4poTVgDzICefGL8Mxj0PUIGH9dslc9GSsPjc3iynrW0IMFmrx1ItI//7wbWJTCf eNWkQa9WmywFUcMQy/oVsWITq7RaoCdYLVQ6w7CrusQ1DOOJ1OoCVA8HB+zqJGKW X6dLTTKF14kYNABPHf7lPd8Zv2VIvxLMxqZuSDKZouQjaciXLI4LGdQje9N9j1JE cInKIzQnI3AfKTd0TdWR1F+j8QAQCBrHuOLO6WQEQ+N82BZX7KFn+0SVzzH0QjpL K+vjHSBZAgMBAAECggEBAKA6qZZC3BIdyGm/7k9dehlRm6CLGnrdEM7J4r8gW8JR NLvTPQbUKljX8/VTqOMZ97Z3lYmlJC4bf9cWSLJ05mIJ5niTWpQvwmB1i4ICJbgy d8ebvo0BW2kj+OxwxrNl6L9BZrcZOQ3yeXWfQgRCyCqbgmeyPHYroAdhKV9V78CF HSHwZOMVPUo/y44w6ig5Fw6pKdGXSzZwjGUyZNpaj4IY3LYWWffuigHJduMzBut2 v4JJC2TPC9hB6MiR/sVqpKxtnM466BB8pv7hlZ9TJFzdfD5k1UZuheUgUiunajiC FeHOEjtSsTWKsAmTg26+Bw5r1apdL3QsLwqH58IVPaUCgYEA1k//XKqWIWG73OKc mOH5gjzb3Aq4c6yw5TGPhEkPn7D+7auKuwzuoUrzASeopzp0te7Ow299fNToFWIs wEXi4g0gezO7UwpJ6wWNr0iL/wPgYx3jglrPQ+Pvmfoaj4N0mK/pfmXHF8RdIs/M p2kn2oQLrJgdeGb4FZM0byX7UcsCgYEAykKJotProJqDogbqsNEIJpBfFlBvxFgb IPAp33tBqBh9DiKIOOtISTjPZQIAkzkC8SMRK+HZbF1p2/20bjvTOsi8PKxm/sqS v/oPLDfni8pkmhfNU6rr0iNMagpoT9kks4qk9h2dRij1fKJ6pa4TBahJx5wQ3Yu0 vMEmy8JdwesCgYEAoSGAg7GWMv8Cei6/QosUR4FuZGCDEiWS0p+Sogk0gAJZiWRi aARvHkH1traUrTbcLTWhq3sVxFdnLzyjHOTukrr/4uGgQ+0GanfAcTuAVnoZqSv9 tDKGhyrHKOPMOH7DmVEZovju2cW/qL7Hxk7fsgF5rYipD6+Lct08nRzXekUCgYEA s2muWYeOnhox5coo6MujZUHvdwXG/u4AsokXO6xEI24FkEJFf+gFaR5BqiHKjM2n tGsc0kY27Y83VfOI17etuZlSkKeFfUIIRs70Io88j53q+11dv3gAU5kIMZAl056U lcbIaaD/X7r5d6NRFCKDsSMEv1HLDBrfKghT967kKB0CgYBnynlp2NRtBgtLNURS c2xQ7NrlhSiWyLKii/h55PRfxps07w/eqsNALgAqBVDvZt8TcD1IW8/FYv+ZcvMm GUA4MNQonauQbLDbkwEHc8MWIpPMJnQYf9YAFlf73qknlikepc+s0mkNkYaZYQYZ Q3uWw4iuk4iyxAzWDWrIRPoiRA== -----END PRIVATE KEY----- ================================================ FILE: ruoyi-admin/src/main/resources/application-dev.yml ================================================ --- # 监控中心配置 spring.boot.admin.client: # 增加客户端开关 enabled: true url: http://localhost:9090/admin instance: service-host-type: IP username: ruoyi password: 123456 --- # 数据源配置 spring: datasource: type: com.zaxxer.hikari.HikariDataSource # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content dynamic: # 性能分析插件(有性能损耗 不建议生产环境使用) p6spy: true # 设置默认的数据源或者数据源组,默认值即为 master primary: master # 严格模式 匹配不到数据源则报错 strict: true datasource: # 主库数据源 master: type: ${spring.datasource.type} driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) url: jdbc:mysql://localhost:3306/paizhicheng?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: root password: 123456 hikari: # 最大连接池数量 maxPoolSize: 20 # 最小空闲线程数量 minIdle: 10 # 配置获取连接等待超时的时间 connectionTimeout: 30000 # 校验超时时间 validationTimeout: 5000 # 空闲连接存活最大时间,默认10分钟 idleTimeout: 600000 # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 maxLifetime: 1800000 # 连接测试query(配置检测连接是否有效) connectionTestQuery: SELECT 1 # 多久检查一次连接的活性 keepaliveTime: 30000 --- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) spring: redis: # 地址 # Redis数据库索引(默认为0) database: 5 # Redis服务器地址 host: localhost # 端口,默认为6379 port: 6379 # 密码(如没有密码请注释掉) # password: # 连接超时时间 timeout: 10s # 是否开启ssl ssl: false redisson: # redis key前缀 keyPrefix: # 线程池数量 threads: 4 # Netty线程池数量 nettyThreads: 8 # 单节点配置 singleServerConfig: # 客户端名称 clientName: ${ruoyi.name} # 最小空闲连接数 connectionMinimumIdleSize: 8 # 连接池大小 connectionPoolSize: 32 # 连接空闲超时,单位:毫秒 idleConnectionTimeout: 10000 # 命令等待超时,单位:毫秒 timeout: 3000 # 发布和订阅连接池大小 subscriptionConnectionPoolSize: 50 ================================================ FILE: ruoyi-admin/src/main/resources/application.yml ================================================ neteaseCloudMusicApi: http://iwenwiki.com:3000/song/url?id= # 网易云音乐API wx: appId: # 小程序AppId appSecret: # 小程序AppSecret notifyUrl: # 微信支付回调地址 merchantId: # 商户号 merchantSerialNumber: # 商户API证书序列号 api3: # 商户API证书序 server: port: 9394 # Websocket服务端口 host: localhost # Websocket服务地址 api: school: https://hn216.api.yesapi.cn/?s=App.Common_University.Search&return_data=0&school_name= # 高校查询API apiKey: top_num=10&app_key=D5B0BA3EDE51C76C1EEC4D581589F3A9&sign=99E25CA458C5389EEA9E2E6530CB825B # API密钥 # 项目相关配置 ruoyi: # 名称 name: RuoYi-Vue-Plus # 版本 version: ${ruoyi-vue-plus.version} # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 获取ip地址开关 addressEnabled: true # 缓存懒加载 cacheLazy: false captcha: # 页面 <参数设置> 可开启关闭 验证码校验 # 验证码类型 math 数组计算 char 字符验证 type: MATH # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 category: CIRCLE # 数字验证码位数 numberLength: 1 # 字符验证码长度 charLength: 4 # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 9393 servlet: # 应用的访问路径 context-path: / # undertow 配置 undertow: # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的 max-http-post-size: -1 # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 # 每块buffer的空间大小,越小的空间被利用越充分 buffer-size: 512 # 是否分配的直接内存 direct-buffers: true threads: # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 io: 8 # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 worker: 256 # 日志配置 logging: level: com.ruoyi: @logging.level@ org.springframework: warn config: classpath:logback-plus.xml # 用户配置 user: password: # 密码最大错误次数 maxRetryCount: 50000 # 密码锁定时间(默认10分钟) lockTime: 10 # Spring配置 spring: application: name: ${ruoyi.name} # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: @profiles.active@ # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 50MB # 设置总上传的文件大小 max-request-size: 50MB # 服务模块 devtools: restart: # 热部署开关 enabled: true mvc: format: date-time: yyyy-MM-dd HH:mm:ss jackson: # 日期格式化 date-format: yyyy-MM-dd HH:mm:ss serialization: # 格式化输出 indent_output: false # 忽略无法转换的对象 fail_on_empty_beans: false deserialization: # 允许对象忽略json中不存在的属性 fail_on_unknown_properties: false # Sa-Token配置 sa-token: # token名称 (同时也是cookie名称) token-name: Authorization # token有效期 设为一天 (必定过期) 单位: 秒 一个月过期(2592000 ) 需要重新登录 -1 为永不过期 timeout: 2592000 # token临时有效期 (指定时间无操作就过期) 单位: 秒 activity-timeout: -1 # 1800 # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) is-concurrent: true # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) is-share: false # 是否尝试从header里读取token is-read-header: true # 是否尝试从cookie里读取token is-read-cookie: false # token前缀 token-prefix: "Bearer" # jwt秘钥 jwt-secret-key: abcdefghiwdwdwjklmnopqrstuvwxyz # security配置 security: # 排除路径 excludes: # 静态资源 - /*.html - /**/*.html - /**/*.css - /**/*.js # 公共路径 - /favicon.ico - /error # swagger 文档配置 - /*/api-docs - /*/api-docs/** # actuator 监控配置 - /actuator - /actuator/** # actuator 监控配置 - /system/activity/listWx # 小程序端 活动列表 - /wx/user/login # 小程序端 登录 - /system/dict/data/list # 字典数据列表 - /wx/user/callback # 小程序端 回调 - /system/viewPager/list # 小程序端 轮播图 - /wx/user/music # 小程序端 音乐列表 - /wx/user/filterKeyWords #敏感词过滤 - /test/** # 测试接口 # MyBatisPlus配置 # https://baomidou.com/config/ mybatis-plus: # 不支持多包, 如有需要可在注解配置 或 提升扫包等级 # 例如 com.**.**.mapper mapperPackage: top.flya.**.mapper # 对应的 XML 文件位置 mapperLocations: classpath*:mapper/**/*Mapper.xml # 实体扫描,多个package用逗号或者分号分隔 typeAliasesPackage: top.flya.**.domain # 启动时是否检查 MyBatis XML 文件的存在,默认不检查 checkConfigLocation: false configuration: # 自动驼峰命名规则(camel case)映射 mapUnderscoreToCamelCase: true # MyBatis 自动映射策略 # NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射 autoMappingBehavior: PARTIAL # MyBatis 自动映射时未知列或未知属性处理策 # NONE:不做处理 WARNING:打印相关警告 FAILING:抛出异常和详细信息 autoMappingUnknownColumnBehavior: NONE # 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl # 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl # 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl global-config: # 是否打印 Logo banner banner: true dbConfig: # 主键类型 # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID idType: AUTO # 逻辑已删除值 logicDeleteValue: 2 # 逻辑未删除值 logicNotDeleteValue: 0 # 字段验证策略之 insert,在 insert 的时候的字段验证策略 # IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL insertStrategy: NOT_NULL # 字段验证策略之 update,在 update 的时候的字段验证策略 updateStrategy: NOT_NULL # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件 where-strategy: NOT_NULL # 数据加密 mybatis-encryptor: # 是否开启加密 enable: false # 默认加密算法 algorithm: BASE64 # 编码方式 BASE64/HEX。默认BASE64 encode: BASE64 # 安全秘钥 对称算法的秘钥 如:AES,SM4 password: # 公私钥 非对称算法的公私钥 如:SM2,RSA publicKey: privateKey: # Swagger配置 swagger: info: # 标题 title: '标题:${ruoyi.name}后台管理系统_接口文档' # 描述 description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' # 版本 version: '版本号: ${ruoyi-vue-plus.version}' # 作者信息 contact: name: Lion Li email: crazylionli@163.com url: https://gitee.com/dromara/RuoYi-Vue-Plus components: # 鉴权方式配置 security-schemes: apiKey: type: APIKEY in: HEADER name: ${sa-token.token-name} springdoc: api-docs: # 是否开启接口文档 enabled: true swagger-ui: # 持久化认证数据 persistAuthorization: true #这里定义了两个分组,可定义多个,也可以不定义 group-configs: - group: 1.演示模块 packages-to-scan: top.flya.demo - group: 2.系统模块 packages-to-scan: top.flya.web - group: 3.代码生成模块 packages-to-scan: top.flya.generator # 防止XSS攻击 xss: # 过滤开关 enabled: false # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* # 全局线程池相关配置 thread-pool: # 是否开启线程池 enabled: false # 队列最大长度 queueCapacity: 128 # 线程池维护线程所允许的空闲时间 keepAliveSeconds: 300 xxl: job: admin: url: update: ${xxl.job.admin-addresses}/jobinfo/update add: ${xxl.job.admin-addresses}/jobinfo/add start: ${xxl.job.admin-addresses}/jobinfo/start stop: ${xxl.job.admin-addresses}/jobinfo/stop pageList: ${xxl.job.admin-addresses}/jobinfo/pageList address: http://111.229.24.163:9100/xxl-job-admin username: admin password: admin enabled: true admin-addresses: http://111.229.24.163:9100/xxl-job-admin access-token: xdsl3ewi3al1oehxmo68pqxer executor: appname: pai-zhi-cheng ip: port: 9101 logpath: ./logs/xxl-job logretentiondays: 30 --- # 分布式锁 lock4j 全局配置 lock4j: # 获取分布式锁超时时间,默认为 3000 毫秒 acquire-timeout: 3000 # 分布式锁的超时时间,默认为 30 秒 expire: 30000 --- # Actuator 监控端点的配置项 #management: # endpoints: # web: # exposure: # include: '*' # enabled-by-default: off # endpoint: # health: # show-details: ALWAYS # logfile: # external-file: ./logs/sys-console.log ================================================ FILE: ruoyi-admin/src/main/resources/banner.txt ================================================ Application Version: ${ruoyi-vue-plus.version} Spring Boot Version: ${spring-boot.version} __________ _____.___.__ ____ ____ __________.__ \______ \__ __ ____\__ | |__| \ \ / /_ __ ____ \______ \ | __ __ ______ | _/ | \/ _ \/ | | | ______ \ Y / | \_/ __ \ ______ | ___/ | | | \/ ___/ | | \ | ( <_> )____ | | /_____/ \ /| | /\ ___/ /_____/ | | | |_| | /\___ \ |____|_ /____/ \____// ______|__| \___/ |____/ \___ > |____| |____/____//____ > \/ \/ \/ \/ ================================================ FILE: ruoyi-admin/src/main/resources/i18n/messages.properties ================================================ #错误消息 not.null=* 必须填写 user.jcaptcha.error=验证码错误 user.jcaptcha.expire=验证码已失效 user.not.exists=对不起, 您的账号:{0} 不存在. user.password.not.match=用户不存在/密码错误 user.password.retry.limit.count=密码输入错误{0}次 user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 user.password.delete=对不起,您的账号:{0} 已被删除 user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 role.blocked=角色已封禁,请联系管理员 user.logout.success=退出成功 length.not.valid=长度必须在{min}到{max}个字符之间 user.username.not.blank=用户名不能为空 user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 user.username.length.valid=账户长度必须在{min}到{max}个字符之间 user.password.not.blank=用户密码不能为空 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.not.valid=* 5-50个字符 user.email.not.valid=邮箱格式错误 user.email.not.blank=邮箱不能为空 user.phonenumber.not.blank=用户手机号不能为空 user.mobile.phone.number.not.valid=手机号格式错误 user.login.success=登录成功 user.register.success=注册成功 user.register.save.error=保存用户 {0} 失败,注册账号已存在 user.register.error=注册失败,请联系系统管理人员 user.notfound=请重新登录 user.forcelogout=管理员强制退出,请重新登录 user.unknown.error=未知错误,请重新登录 ##文件上传消息 upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{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}] repeat.submit.message=不允许重复提交,请稍候再试 rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 email.code.not.blank=邮箱验证码不能为空 email.code.retry.limit.count=邮箱验证码输入错误{0}次 email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 xcx.code.not.blank=小程序code不能为空 ================================================ FILE: ruoyi-admin/src/main/resources/i18n/messages_en_US.properties ================================================ #错误消息 not.null=* Required fill in user.jcaptcha.error=Captcha error user.jcaptcha.expire=Captcha invalid user.not.exists=Sorry, your account: {0} does not exist user.password.not.match=User does not exist/Password error user.password.retry.limit.count=Password input error {0} times user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes user.password.delete=Sorry, your account:{0} has been deleted user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator role.blocked=Role disabled,please contact administrators user.logout.success=Exit successful length.not.valid=The length must be between {min} and {max} characters user.username.not.blank=Username cannot be blank user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number user.username.length.valid=Account length must be between {min} and {max} characters user.password.not.blank=Password cannot be empty user.password.length.valid=Password length must be between {min} and {max} characters user.password.not.valid=* 5-50 characters user.email.not.valid=Mailbox format error user.email.not.blank=Mailbox cannot be blank user.phonenumber.not.blank=Phone number cannot be blank user.mobile.phone.number.not.valid=Phone number format error user.login.success=Login successful user.register.success=Register successful user.register.save.error=Failed to save user {0}, The registered account already exists user.register.error=Register failed, please contact system administrator user.notfound=Please login again user.forcelogout=The administrator is forced to exit,please login again user.unknown.error=Unknown error, please login again ##文件上传消息 upload.exceed.maxSize=The uploaded file size exceeds the limit file size!
the maximum allowed file size is:{0}MB! upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters ##权限 no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}] no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}] no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}] no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}] no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}] no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}] repeat.submit.message=Repeat submit is not allowed, please try again later rate.limiter.message=Visit too frequently, please try again later sms.code.not.blank=Sms code cannot be blank sms.code.retry.limit.count=Sms code input error {0} times sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes email.code.not.blank=Email code cannot be blank email.code.retry.limit.count=Email code input error {0} times email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes xcx.code.not.blank=Mini program code cannot be blank ================================================ FILE: ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties ================================================ #错误消息 not.null=* 必须填写 user.jcaptcha.error=验证码错误 user.jcaptcha.expire=验证码已失效 user.not.exists=对不起, 您的账号:{0} 不存在. user.password.not.match=用户不存在/密码错误 user.password.retry.limit.count=密码输入错误{0}次 user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 user.password.delete=对不起,您的账号:{0} 已被删除 user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 role.blocked=角色已封禁,请联系管理员 user.logout.success=退出成功 length.not.valid=长度必须在{min}到{max}个字符之间 user.username.not.blank=用户名不能为空 user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 user.username.length.valid=账户长度必须在{min}到{max}个字符之间 user.password.not.blank=用户密码不能为空 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.not.valid=* 5-50个字符 user.email.not.valid=邮箱格式错误 user.email.not.blank=邮箱不能为空 user.phonenumber.not.blank=用户手机号不能为空 user.mobile.phone.number.not.valid=手机号格式错误 user.login.success=登录成功 user.register.success=注册成功 user.register.save.error=保存用户 {0} 失败,注册账号已存在 user.register.error=注册失败,请联系系统管理人员 user.notfound=请重新登录 user.forcelogout=管理员强制退出,请重新登录 user.unknown.error=未知错误,请重新登录 ##文件上传消息 upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{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}] repeat.submit.message=不允许重复提交,请稍候再试 rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 email.code.not.blank=邮箱验证码不能为空 email.code.retry.limit.count=邮箱验证码输入错误{0}次 email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 xcx.code.not.blank=小程序code不能为空 ================================================ FILE: ruoyi-admin/src/main/resources/ip2region.xdb ================================================ [File too large to display: 10.6 MB] ================================================ FILE: ruoyi-admin/src/main/resources/logback-plus.xml ================================================ ${console.log.pattern} utf-8 ${log.path}/sys-console.log ${log.path}/sys-console.%d{yyyy-MM-dd}.log 1 ${log.pattern} utf-8 INFO ${log.path}/sys-info.log ${log.path}/sys-info.%d{yyyy-MM-dd}.log 60 ${log.pattern} INFO ACCEPT DENY ${log.path}/sys-error.log ${log.path}/sys-error.%d{yyyy-MM-dd}.log 60 ${log.pattern} ERROR ACCEPT DENY 0 512 0 512 ================================================ FILE: ruoyi-admin/src/main/resources/spy.properties ================================================ # p6spy 性能分析插件配置文件 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 #deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # SQL语句打印时间格式 databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2 # 是否过滤 Log filter=true # 过滤 Log 时所排除的 sql 关键字,以逗号分隔 exclude=SELECT 1 ================================================ FILE: ruoyi-common/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 ruoyi-common common通用工具 org.springframework spring-context-support org.springframework spring-web cn.dev33 sa-token-spring-boot-starter cn.dev33 sa-token-jwt org.springframework.boot spring-boot-starter-validation org.apache.commons commons-lang3 com.fasterxml.jackson.core jackson-databind com.alibaba easyexcel org.yaml snakeyaml javax.servlet javax.servlet-api com.baomidou mybatis-plus-boot-starter com.baomidou dynamic-datasource-spring-boot-starter cn.hutool hutool-core cn.hutool hutool-http cn.hutool hutool-captcha cn.hutool hutool-jwt cn.hutool hutool-extra com.sun.mail jakarta.mail org.projectlombok lombok org.springdoc springdoc-openapi-webmvc-core org.springdoc springdoc-openapi-javadoc org.springframework.boot spring-boot-configuration-processor org.redisson redisson-spring-boot-starter org.redisson redisson-spring-data-27 com.baomidou lock4j-redisson-spring-boot-starter org.bouncycastle bcprov-jdk15to18 org.lionsoul ip2region ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/CellMerge.java ================================================ package top.flya.common.annotation; import top.flya.common.excel.CellMergeStrategy; import java.lang.annotation.*; /** * excel 列单元格合并(合并列相同项) * * 需搭配 {@link CellMergeStrategy} 策略使用 * * @author Lion Li */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface CellMerge { /** * col index */ int index() default -1; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/DataColumn.java ================================================ package top.flya.common.annotation; import java.lang.annotation.*; /** * 数据权限 * * 一个注解只能对应一个模板 * * @author Lion Li * @version 3.5.0 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataColumn { /** * 占位符关键字 */ String[] key() default "deptName"; /** * 占位符替换值 */ String[] value() default "dept_id"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/DataPermission.java ================================================ package top.flya.common.annotation; import java.lang.annotation.*; /** * 数据权限组 * * @author Lion Li * @version 3.5.0 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataPermission { DataColumn[] value(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/DictDataMapper.java ================================================ package top.flya.common.annotation; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import top.flya.common.jackson.DictDataJsonSerializer; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 字典数据映射注解 * * @author itino * @deprecated 建议使用通用翻译注解 */ @Deprecated @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) @JacksonAnnotationsInside @JsonSerialize(using = DictDataJsonSerializer.class) public @interface DictDataMapper { /** * 设置字典的type值 (如: sys_user_sex) */ String dictType() default ""; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/EncryptField.java ================================================ package top.flya.common.annotation; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import java.lang.annotation.*; /** * 字段加密注解 * * @author 老马 */ @Documented @Inherited @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface EncryptField { /** * 加密算法 */ AlgorithmType algorithm() default AlgorithmType.DEFAULT; /** * 秘钥。AES、SM4需要 */ String password() default ""; /** * 公钥。RSA、SM2需要 */ String publicKey() default ""; /** * 公钥。RSA、SM2需要 */ String privateKey() default ""; /** * 编码方式。对加密算法为BASE64的不起作用 */ EncodeType encode() default EncodeType.DEFAULT; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/ExcelDictFormat.java ================================================ package top.flya.common.annotation; import top.flya.common.utils.StringUtils; import java.lang.annotation.*; /** * 字典格式化 * * @author Lion Li */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface ExcelDictFormat { /** * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) */ String dictType() default ""; /** * 读取内容转表达式 (如: 0=男,1=女,2=未知) */ String readConverterExp() default ""; /** * 分隔符,读取字符串组内容 */ String separator() default StringUtils.SEPARATOR; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/ExcelEnumFormat.java ================================================ package top.flya.common.annotation; import java.lang.annotation.*; /** * 枚举格式化 * * @author Liang */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface ExcelEnumFormat { /** * 字典枚举类型 */ Class> enumClass(); /** * 字典枚举类中对应的code属性名称,默认为code */ String codeField() default "code"; /** * 字典枚举类中对应的text属性名称,默认为text */ String textField() default "text"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/Log.java ================================================ package top.flya.common.annotation; import top.flya.common.enums.BusinessType; import top.flya.common.enums.OperatorType; import java.lang.annotation.*; /** * 自定义操作日志记录注解 * * @author ruoyi */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** * 模块 */ String title() default ""; /** * 功能 */ BusinessType businessType() default BusinessType.OTHER; /** * 操作人类别 */ OperatorType operatorType() default OperatorType.MANAGE; /** * 是否保存请求的参数 */ boolean isSaveRequestData() default true; /** * 是否保存响应的参数 */ boolean isSaveResponseData() default true; /** * 排除指定的请求参数 */ String[] excludeParamNames() default {}; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/RateLimiter.java ================================================ package top.flya.common.annotation; import top.flya.common.enums.LimitType; import java.lang.annotation.*; /** * 限流注解 * * @author Lion Li */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimiter { /** * 限流key,支持使用Spring el表达式来动态获取方法上的参数值 * 格式类似于 #code.id #{#code} */ String key() default ""; /** * 限流时间,单位秒 */ int time() default 60; /** * 限流次数 */ int count() default 100; /** * 限流类型 */ LimitType limitType() default LimitType.DEFAULT; /** * 提示消息 支持国际化 格式为 {code} */ String message() default "{rate.limiter.message}"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/RepeatSubmit.java ================================================ package top.flya.common.annotation; import java.lang.annotation.*; import java.util.concurrent.TimeUnit; /** * 自定义注解防止表单重复提交 * * @author Lion Li */ @Inherited @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RepeatSubmit { /** * 间隔时间(ms),小于此时间视为重复提交 */ int interval() default 5000; TimeUnit timeUnit() default TimeUnit.MILLISECONDS; /** * 提示消息 支持国际化 格式为 {code} */ String message() default "{repeat.submit.message}"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/Sensitive.java ================================================ package top.flya.common.annotation; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import top.flya.common.enums.SensitiveStrategy; import top.flya.common.jackson.SensitiveJsonSerializer; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 数据脱敏注解 * * @author zhujie */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @JacksonAnnotationsInside @JsonSerialize(using = SensitiveJsonSerializer.class) public @interface Sensitive { SensitiveStrategy strategy(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/Translation.java ================================================ package top.flya.common.annotation; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import top.flya.common.translation.handler.TranslationHandler; import java.lang.annotation.*; /** * 通用翻译注解 * * @author Lion Li */ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) @Documented @JacksonAnnotationsInside @JsonSerialize(using = TranslationHandler.class) public @interface Translation { /** * 类型 (需与实现类上的 {@link TranslationType} 注解type对应) *

* 默认取当前字段的值 如果设置了 @{@link Translation#mapper()} 则取映射字段的值 */ String type(); /** * 映射字段 (如果不为空则取此字段的值) */ String mapper() default ""; /** * 其他条件 例如: 字典type(sys_user_sex) */ String other() default ""; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/annotation/TranslationType.java ================================================ package top.flya.common.annotation; import top.flya.common.translation.TranslationInterface; import java.lang.annotation.*; /** * 翻译类型注解 (标注到{@link TranslationInterface} 的实现类) * * @author Lion Li */ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented public @interface TranslationType { /** * 类型 */ String type(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/captcha/UnsignedMathGenerator.java ================================================ package top.flya.common.captcha; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.math.Calculator; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.RandomUtil; import top.flya.common.utils.StringUtils; /** * 无符号计算生成器 * * @author Lion Li */ public class UnsignedMathGenerator implements CodeGenerator { private static final long serialVersionUID = -5514819971774091076L; private static final String OPERATORS = "+-*"; /** * 参与计算数字最大长度 */ private final int numberLength; /** * 构造 */ public UnsignedMathGenerator() { this(2); } /** * 构造 * * @param numberLength 参与计算最大数字位数 */ public UnsignedMathGenerator(int numberLength) { this.numberLength = numberLength; } @Override public String generate() { final int limit = getLimit(); int a = RandomUtil.randomInt(limit); int b = RandomUtil.randomInt(limit); String max = Integer.toString(Math.max(a,b)); String min = Integer.toString(Math.min(a,b)); max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE); min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE); return max + RandomUtil.randomChar(OPERATORS) + min + '='; } @Override public boolean verify(String code, String userInputCode) { int result; try { result = Integer.parseInt(userInputCode); } catch (NumberFormatException e) { // 用户输入非数字 return false; } final int calculateResult = (int) Calculator.conversion(code); return result == calculateResult; } /** * 获取验证码长度 * * @return 验证码长度 */ public int getLength() { return this.numberLength * 2 + 2; } /** * 根据长度获取参与计算数字最大值 * * @return 最大值 */ private int getLimit() { return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength)); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/config/RuoYiConfig.java ================================================ package top.flya.common.config; import lombok.Data; import lombok.Getter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 读取项目相关配置 * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "ruoyi") public class RuoYiConfig { /** * 项目名称 */ private String name; /** * 版本 */ private String version; /** * 版权年份 */ private String copyrightYear; /** * 实例演示开关 */ private boolean demoEnabled; /** * 缓存懒加载 */ private boolean cacheLazy; /** * 获取地址开关 */ @Getter private static boolean addressEnabled; public void setAddressEnabled(boolean addressEnabled) { RuoYiConfig.addressEnabled = addressEnabled; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/constant/CacheConstants.java ================================================ package top.flya.common.constant; /** * 缓存的key 常量 * * @author ruoyi */ public interface CacheConstants { /** * 在线用户 redis key */ String ONLINE_TOKEN_KEY = "online_tokens:"; /** * 验证码 redis key */ String CAPTCHA_CODE_KEY = "captcha_codes:"; /** * 参数管理 cache key */ String SYS_CONFIG_KEY = "sys_config:"; /** * 字典管理 cache key */ String SYS_DICT_KEY = "sys_dict:"; /** * 防重提交 redis key */ String REPEAT_SUBMIT_KEY = "repeat_submit:"; /** * 限流 redis key */ String RATE_LIMIT_KEY = "rate_limit:"; /** * 登录账户密码错误次数 redis key */ String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/constant/CacheNames.java ================================================ package top.flya.common.constant; /** * 缓存组名称常量 *

* key 格式为 cacheNames#ttl#maxIdleTime#maxSize *

* ttl 过期时间 如果设置为0则不过期 默认为0 * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 *

* 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 * * @author Lion Li */ public interface CacheNames { /** * 演示案例 */ String DEMO_CACHE = "demo:cache#60s#10m#20"; /** * 系统配置 */ String SYS_CONFIG = "sys_config"; /** * 数据字典 */ String SYS_DICT = "sys_dict"; /** * 用户账户 */ String SYS_USER_NAME = "sys_user_name#30d"; /** * 部门 */ String SYS_DEPT = "sys_dept#30d"; /** * OSS内容 */ String SYS_OSS = "sys_oss#30d"; /** * OSS配置 */ String SYS_OSS_CONFIG = "sys_oss_config"; /** * 在线用户 */ String ONLINE_TOKEN = "online_tokens"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/constant/Constants.java ================================================ package top.flya.common.constant; /** * 通用常量信息 * * @author ruoyi */ public interface Constants { /** * UTF-8 字符集 */ String UTF8 = "UTF-8"; /** * GBK 字符集 */ String GBK = "GBK"; /** * www主域 */ String WWW = "www."; /** * http请求 */ String HTTP = "http://"; /** * https请求 */ String HTTPS = "https://"; /** * 通用成功标识 */ String SUCCESS = "0"; /** * 通用失败标识 */ String FAIL = "1"; /** * 登录成功 */ String LOGIN_SUCCESS = "Success"; /** * 注销 */ String LOGOUT = "Logout"; /** * 注册 */ String REGISTER = "Register"; /** * 登录失败 */ String LOGIN_FAIL = "Error"; /** * 验证码有效期(分钟) */ Integer CAPTCHA_EXPIRATION = 2; /** * 令牌 */ String TOKEN = "token"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/constant/GenConstants.java ================================================ package top.flya.common.constant; /** * 代码生成通用常量 * * @author ruoyi */ public interface GenConstants { /** * 单表(增删改查) */ String TPL_CRUD = "crud"; /** * 树表(增删改查) */ String TPL_TREE = "tree"; /** * 主子表(增删改查) */ String TPL_SUB = "sub"; /** * 树编码字段 */ String TREE_CODE = "treeCode"; /** * 树父编码字段 */ String TREE_PARENT_CODE = "treeParentCode"; /** * 树名称字段 */ String TREE_NAME = "treeName"; /** * 上级菜单ID字段 */ String PARENT_MENU_ID = "parentMenuId"; /** * 上级菜单名称字段 */ String PARENT_MENU_NAME = "parentMenuName"; /** * 数据库字符串类型 */ String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"}; /** * 数据库文本类型 */ String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"}; /** * 数据库时间类型 */ String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"}; /** * 数据库数字类型 */ String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer", "bit", "bigint", "float", "double", "decimal"}; /** * BO对象 不需要添加字段 */ String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by", "update_time", "version"}; /** * BO对象 不需要编辑字段 */ String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by", "update_time", "version"}; /** * VO对象 不需要返回字段 */ String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by", "update_time", "version"}; /** * BO对象 不需要查询字段 */ String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by", "update_time", "remark", "version"}; /** * Entity基类字段 */ String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"}; /** * Tree基类字段 */ String[] TREE_ENTITY = {"parentName", "parentId", "children"}; /** * 文本框 */ String HTML_INPUT = "input"; /** * 文本域 */ String HTML_TEXTAREA = "textarea"; /** * 下拉框 */ String HTML_SELECT = "select"; /** * 单选框 */ String HTML_RADIO = "radio"; /** * 复选框 */ String HTML_CHECKBOX = "checkbox"; /** * 日期控件 */ String HTML_DATETIME = "datetime"; /** * 图片上传控件 */ String HTML_IMAGE_UPLOAD = "imageUpload"; /** * 文件上传控件 */ String HTML_FILE_UPLOAD = "fileUpload"; /** * 富文本控件 */ String HTML_EDITOR = "editor"; /** * 字符串类型 */ String TYPE_STRING = "String"; /** * 整型 */ String TYPE_INTEGER = "Integer"; /** * 长整型 */ String TYPE_LONG = "Long"; /** * 浮点型 */ String TYPE_DOUBLE = "Double"; /** * 高精度计算类型 */ String TYPE_BIGDECIMAL = "BigDecimal"; /** * 时间类型 */ String TYPE_DATE = "Date"; /** * 模糊查询 */ String QUERY_LIKE = "LIKE"; /** * 相等查询 */ String QUERY_EQ = "EQ"; /** * 需要 */ String REQUIRE = "1"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/constant/HttpStatus.java ================================================ package top.flya.common.constant; /** * 返回状态码 * * @author Lion Li */ public interface HttpStatus { /** * 操作成功 */ int SUCCESS = 200; /** * 对象创建成功 */ int CREATED = 201; /** * 请求已经被接受 */ int ACCEPTED = 202; /** * 操作已经执行成功,但是没有返回数据 */ int NO_CONTENT = 204; /** * 资源已被移除 */ int MOVED_PERM = 301; /** * 重定向 */ int SEE_OTHER = 303; /** * 资源没有被修改 */ int NOT_MODIFIED = 304; /** * 参数列表错误(缺少,格式不匹配) */ int BAD_REQUEST = 400; /** * 未授权 */ int UNAUTHORIZED = 401; /** * 访问受限,授权过期 */ int FORBIDDEN = 403; /** * 资源,服务未找到 */ int NOT_FOUND = 404; /** * 不允许的http方法 */ int BAD_METHOD = 405; /** * 资源冲突,或者资源被锁 */ int CONFLICT = 409; /** * 不支持的数据,媒体类型 */ int UNSUPPORTED_TYPE = 415; /** * 系统内部错误 */ int ERROR = 500; /** * 接口未实现 */ int NOT_IMPLEMENTED = 501; /** * 系统警告消息 */ int WARN = 601; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/constant/TransConstant.java ================================================ package top.flya.common.constant; /** * 翻译常量 * * @author Lion Li */ public interface TransConstant { /** * 用户id转账号 */ String USER_ID_TO_NAME = "user_id_to_name"; /** * 部门id转名称 */ String DEPT_ID_TO_NAME = "dept_id_to_name"; /** * 字典type转label */ String DICT_TYPE_TO_LABEL = "dict_type_to_label"; /** * ossId转url */ String OSS_ID_TO_URL = "oss_id_to_url"; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/constant/UserConstants.java ================================================ package top.flya.common.constant; /** * 用户常量信息 * * @author ruoyi */ public interface UserConstants { /** * 平台内系统用户的唯一标志 */ String SYS_USER = "SYS_USER"; /** * 正常状态 */ String NORMAL = "0"; /** * 异常状态 */ String EXCEPTION = "1"; /** * 用户正常状态 */ String USER_NORMAL = "0"; /** * 用户封禁状态 */ String USER_DISABLE = "1"; /** * 角色正常状态 */ String ROLE_NORMAL = "0"; /** * 角色封禁状态 */ String ROLE_DISABLE = "1"; /** * 部门正常状态 */ String DEPT_NORMAL = "0"; /** * 部门停用状态 */ String DEPT_DISABLE = "1"; /** * 字典正常状态 */ String DICT_NORMAL = "0"; /** * 是否为系统默认(是) */ String YES = "Y"; /** * 是否菜单外链(是) */ String YES_FRAME = "0"; /** * 是否菜单外链(否) */ String NO_FRAME = "1"; /** * 菜单正常状态 */ String MENU_NORMAL = "0"; /** * 菜单停用状态 */ String MENU_DISABLE = "1"; /** * 菜单类型(目录) */ String TYPE_DIR = "M"; /** * 菜单类型(菜单) */ String TYPE_MENU = "C"; /** * 菜单类型(按钮) */ String TYPE_BUTTON = "F"; /** * Layout组件标识 */ String LAYOUT = "Layout"; /** * ParentView组件标识 */ String PARENT_VIEW = "ParentView"; /** * InnerLink组件标识 */ String INNER_LINK = "InnerLink"; /** * 用户名长度限制 */ int USERNAME_MIN_LENGTH = 2; int USERNAME_MAX_LENGTH = 20; /** * 密码长度限制 */ int PASSWORD_MIN_LENGTH = 5; int PASSWORD_MAX_LENGTH = 20; /** * 管理员ID */ Long ADMIN_ID = 1L; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/convert/ExcelBigNumberConvert.java ================================================ package top.flya.common.convert; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; 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 lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; /** * 大数值转换 * Excel 数值长度位15位 大于15位的数值转换位字符串 * * @author Lion Li */ @Slf4j public class ExcelBigNumberConvert implements Converter { @Override public Class supportJavaTypeKey() { return Long.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } @Override public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { return Convert.toLong(cellData.getData()); } @Override public WriteCellData convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (ObjectUtil.isNotNull(object)) { String str = Convert.toStr(object); if (str.length() > 15) { return new WriteCellData<>(str); } } WriteCellData cellData = new WriteCellData<>(new BigDecimal(object)); cellData.setType(CellDataTypeEnum.NUMBER); return cellData; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/convert/ExcelDictConvert.java ================================================ package top.flya.common.convert; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; 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 top.flya.common.annotation.ExcelDictFormat; import top.flya.common.core.service.DictService; import top.flya.common.utils.StringUtils; import top.flya.common.utils.poi.ExcelUtil; import top.flya.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; /** * 字典格式化转换处理 * * @author Lion Li */ @Slf4j public class ExcelDictConvert implements Converter { @Override public Class supportJavaTypeKey() { return Object.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return null; } @Override public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { ExcelDictFormat anno = getAnnotation(contentProperty.getField()); String type = anno.dictType(); String label = cellData.getStringValue(); String value; if (StringUtils.isBlank(type)) { value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator()); } else { value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator()); } return Convert.convert(contentProperty.getField().getType(), value); } @Override public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (ObjectUtil.isNull(object)) { return new WriteCellData<>(""); } ExcelDictFormat anno = getAnnotation(contentProperty.getField()); String type = anno.dictType(); String value = Convert.toStr(object); String label; if (StringUtils.isBlank(type)) { label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator()); } else { label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator()); } return new WriteCellData<>(label); } private ExcelDictFormat getAnnotation(Field field) { return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/convert/ExcelEnumConvert.java ================================================ package top.flya.common.convert; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; 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 top.flya.common.annotation.ExcelEnumFormat; import top.flya.common.utils.reflect.ReflectUtils; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; /** * 枚举格式化转换处理 * * @author Liang */ @Slf4j public class ExcelEnumConvert implements Converter { @Override public Class supportJavaTypeKey() { return Object.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return null; } @Override public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { Object codeValue = cellData.getData(); // 如果是空值 if (ObjectUtil.isNull(codeValue)) { return null; } Map enumValueMap = beforeConvert(contentProperty); String textValue = enumValueMap.get(codeValue); return Convert.convert(contentProperty.getField().getType(), textValue); } @Override public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (ObjectUtil.isNull(object)) { return new WriteCellData<>(""); } Map enumValueMap = beforeConvert(contentProperty); String value = Convert.toStr(enumValueMap.get(object), ""); return new WriteCellData<>(value); } private Map beforeConvert(ExcelContentProperty contentProperty) { ExcelEnumFormat anno = getAnnotation(contentProperty.getField()); Map enumValueMap = new HashMap<>(); Enum[] enumConstants = anno.enumClass().getEnumConstants(); for (Enum enumConstant : enumConstants) { Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField()); String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField()); enumValueMap.put(codeValue, textValue); } return enumValueMap; } private ExcelEnumFormat getAnnotation(Field field) { return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/controller/BaseController.java ================================================ package top.flya.common.core.controller; import top.flya.common.core.domain.R; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StringUtils; /** * web层通用数据处理 * * @author Lion Li */ public class BaseController { /** * 响应返回结果 * * @param rows 影响行数 * @return 操作结果 */ protected R toAjax(int rows) { return rows > 0 ? R.ok() : R.fail(); } /** * 响应返回结果 * * @param result 结果 * @return 操作结果 */ protected R toAjax(boolean result) { return result ? R.ok() : R.fail(); } /** * 页面跳转 */ public String redirect(String url) { return StringUtils.format("redirect:{}", url); } /** * 获取用户缓存信息 */ public LoginUser getLoginUser() { return LoginHelper.getLoginUser(); } /** * 获取登录用户id */ public Long getUserId() { return LoginHelper.getUserId(); } /** * 获取登录部门id */ public Long getDeptId() { return LoginHelper.getDeptId(); } /** * 获取登录用户名 */ public String getUsername() { return LoginHelper.getUsername(); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/BaseEntity.java ================================================ package top.flya.common.core.domain; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * Entity基类 * * @author Lion Li */ @Data public class BaseEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 搜索值 */ @JsonIgnore @TableField(exist = false) private String searchValue; // /** // * 创建者 // */ // @TableField(fill = FieldFill.INSERT) // private String createBy; /** * 创建时间 */ @TableField(fill = FieldFill.INSERT) private Date createTime; // /** // * 更新者 // */ // @TableField(fill = FieldFill.INSERT_UPDATE) // private String updateBy; /** * 更新时间 */ @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; /** * 请求参数 */ @JsonInclude(JsonInclude.Include.NON_EMPTY) @TableField(exist = false) private Map params = new HashMap<>(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/PageQuery.java ================================================ package top.flya.common.core.domain; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.exception.ServiceException; import top.flya.common.utils.StringUtils; import top.flya.common.utils.sql.SqlUtil; import lombok.Data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * 分页查询实体类 * * @author Lion Li */ @Data public class PageQuery implements Serializable { private static final long serialVersionUID = 1L; /** * 分页大小 */ private Integer pageSize; /** * 当前页数 */ private Integer pageNum; /** * 排序列 */ private String orderByColumn; /** * 排序的方向desc或者asc */ private String isAsc; /** * 当前记录起始索引 默认值 */ public static final int DEFAULT_PAGE_NUM = 1; /** * 每页显示记录数 默认值 默认查全部 */ public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; public Page build() { Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); if (pageNum <= 0) { pageNum = DEFAULT_PAGE_NUM; } Page page = new Page<>(pageNum, pageSize); List orderItems = buildOrderItem(); if (CollUtil.isNotEmpty(orderItems)) { page.addOrder(orderItems); } return page; } /** * 构建排序 * * 支持的用法如下: * {isAsc:"asc",orderByColumn:"id"} order by id asc * {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc * {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc * {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc */ private List buildOrderItem() { if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) { // orderByColumn="create_time"; // isAsc="desc"; return null; } String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); orderBy = StringUtils.toUnderScoreCase(orderBy); // 兼容前端排序类型 isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); String[] orderByArr = orderBy.split(StringUtils.SEPARATOR); String[] isAscArr = isAsc.split(StringUtils.SEPARATOR); if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) { throw new ServiceException("排序参数有误"); } List list = new ArrayList<>(); // 每个字段各自排序 for (int i = 0; i < orderByArr.length; i++) { String orderByStr = orderByArr[i]; String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i]; if ("asc".equals(isAscStr)) { list.add(OrderItem.asc(orderByStr)); } else if ("desc".equals(isAscStr)) { list.add(OrderItem.desc(orderByStr)); } else { throw new ServiceException("排序参数有误"); } } return list; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/R.java ================================================ package top.flya.common.core.domain; import top.flya.common.constant.HttpStatus; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * 响应信息主体 * * @author Lion Li */ @Data @NoArgsConstructor public class R implements Serializable { private static final long serialVersionUID = 1L; /** * 成功 */ public static final int SUCCESS = 200; /** * 失败 */ public static final int FAIL = 500; private int code; private String msg; private T data; public static R ok() { return restResult(null, SUCCESS, "操作成功"); } public static R ok(T data) { return restResult(data, SUCCESS, "操作成功"); } public static R ok(String msg) { return restResult(null, SUCCESS, msg); } public static R ok(String msg, T data) { return restResult(data, SUCCESS, msg); } public static R fail() { return restResult(null, FAIL, "操作失败"); } public static R fail(String msg) { return restResult(null, FAIL, msg); } public static R fail(T data) { return restResult(data, FAIL, "操作失败"); } public static R fail(String msg, T data) { return restResult(data, FAIL, msg); } public static R fail(int code, String msg) { return restResult(null, code, msg); } /** * 返回警告消息 * * @param msg 返回内容 * @return 警告消息 */ public static R warn(String msg) { return restResult(null, HttpStatus.WARN, msg); } /** * 返回警告消息 * * @param msg 返回内容 * @param data 数据对象 * @return 警告消息 */ public static R warn(String msg, T data) { return restResult(data, HttpStatus.WARN, msg); } private static R restResult(T data, int code, String msg) { R r = new R<>(); r.setCode(code); r.setData(data); r.setMsg(msg); return r; } public static Boolean isError(R ret) { return !isSuccess(ret); } public static Boolean isSuccess(R ret) { return R.SUCCESS == ret.getCode(); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/TreeEntity.java ================================================ package top.flya.common.core.domain; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.ArrayList; import java.util.List; /** * Tree基类 * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) public class TreeEntity extends BaseEntity { private static final long serialVersionUID = 1L; /** * 父菜单名称 */ @TableField(exist = false) private String parentName; /** * 父菜单ID */ private Long parentId; /** * 子部门 */ @TableField(exist = false) private List children = new ArrayList<>(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/dto/RoleDTO.java ================================================ package top.flya.common.core.domain.dto; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * 角色 * * @author Lion Li */ @Data @NoArgsConstructor public class RoleDTO implements Serializable { /** * 角色ID */ private Long roleId; /** * 角色名称 */ private String roleName; /** * 角色权限 */ private String roleKey; /** * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ private String dataScope; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/dto/UserOnlineDTO.java ================================================ package top.flya.common.core.domain.dto; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * 当前在线会话 * * @author ruoyi */ @Data @NoArgsConstructor public class UserOnlineDTO implements Serializable { private static final long serialVersionUID = 1L; /** * 会话编号 */ 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; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysDept.java ================================================ package top.flya.common.core.domain.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.core.domain.TreeEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * 部门表 sys_dept * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_dept") public class SysDept extends TreeEntity { private static final long serialVersionUID = 1L; /** * 部门ID */ @TableId(value = "dept_id") private Long deptId; /** * 部门名称 */ @NotBlank(message = "部门名称不能为空") @Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符") private String deptName; /** * 显示顺序 */ @NotNull(message = "显示顺序不能为空") private Integer orderNum; /** * 负责人 */ private String leader; /** * 联系电话 */ @Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符") private String phone; /** * 邮箱 */ @Email(message = "邮箱格式不正确") @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") private String email; /** * 部门状态:0正常,1停用 */ private String status; /** * 删除标志(0代表存在 2代表删除) */ @TableLogic private String delFlag; /** * 祖级列表 */ private String ancestors; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysDictData.java ================================================ package top.flya.common.core.domain.entity; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.constant.UserConstants; import top.flya.common.convert.ExcelDictConvert; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; /** * 字典数据表 sys_dict_data * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_dict_data") @ExcelIgnoreUnannotated public class SysDictData extends BaseEntity { /** * 字典编码 */ @ExcelProperty(value = "字典编码") @TableId(value = "dict_code", type = IdType.AUTO) private Long dictCode; /** * 字典排序 */ @ExcelProperty(value = "字典排序") private Integer dictSort; /** * 字典标签 */ @ExcelProperty(value = "字典标签") @NotBlank(message = "字典标签不能为空") @Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符") private String dictLabel; /** * 字典键值 */ @ExcelProperty(value = "字典键值") @NotBlank(message = "字典键值不能为空") @Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符") private String dictValue; /** * 字典类型 */ @ExcelProperty(value = "字典类型") @NotBlank(message = "字典类型不能为空") @Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符") private String dictType; /** * 样式属性(其他样式扩展) */ @Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符") private String cssClass; /** * 表格字典样式 */ private String listClass; /** * 是否默认(Y是 N否) */ @ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_yes_no") private String isDefault; /** * 状态(0正常 1停用) */ @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_normal_disable") private String status; /** * 备注 */ private String remark; public boolean getDefault() { return UserConstants.YES.equals(this.isDefault); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysDictType.java ================================================ package top.flya.common.core.domain.entity; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; /** * 字典类型表 sys_dict_type * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_dict_type") @ExcelIgnoreUnannotated public class SysDictType extends BaseEntity { /** * 字典主键 */ @ExcelProperty(value = "字典主键") @TableId(value = "dict_id", type = IdType.AUTO) private Long dictId; /** * 字典名称 */ @ExcelProperty(value = "字典名称") @NotBlank(message = "字典名称不能为空") @Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符") private String dictName; /** * 字典类型 */ @ExcelProperty(value = "字典类型") @NotBlank(message = "字典类型不能为空") @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符") @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") private String dictType; /** * 状态(0正常 1停用) */ @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_normal_disable") private String status; /** * 备注 */ private String remark; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysMenu.java ================================================ package top.flya.common.core.domain.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonInclude; import top.flya.common.core.domain.TreeEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * 菜单权限表 sys_menu * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_menu") public class SysMenu extends TreeEntity { /** * 菜单ID */ @TableId(value = "menu_id", type = IdType.AUTO) private Long menuId; /** * 菜单名称 */ @NotBlank(message = "菜单名称不能为空") @Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符") private String menuName; /** * 显示顺序 */ @NotNull(message = "显示顺序不能为空") private Integer orderNum; /** * 路由地址 */ @Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符") private String path; /** * 组件路径 */ @Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符") private String component; /** * 路由参数 */ private String queryParam; /** * 是否为外链(0是 1否) */ private String isFrame; /** * 是否缓存(0缓存 1不缓存) */ private String isCache; /** * 类型(M目录 C菜单 F按钮) */ @NotBlank(message = "菜单类型不能为空") private String menuType; /** * 显示状态(0显示 1隐藏) */ private String visible; /** * 菜单状态(0正常 1停用) */ private String status; /** * 权限字符串 */ @JsonInclude(JsonInclude.Include.NON_NULL) @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符") private String perms; /** * 菜单图标 */ private String icon; /** * 备注 */ private String remark; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysRole.java ================================================ package top.flya.common.core.domain.entity; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.*; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.constant.UserConstants; import top.flya.common.convert.ExcelDictConvert; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * 角色表 sys_role * * @author Lion Li */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @TableName("sys_role") @ExcelIgnoreUnannotated public class SysRole extends BaseEntity { /** * 角色ID */ @ExcelProperty(value = "角色序号") @TableId(value = "role_id",type = IdType.AUTO) private Long roleId; /** * 角色名称 */ @ExcelProperty(value = "角色名称") @NotBlank(message = "角色名称不能为空") @Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符") private String roleName; /** * 角色权限 */ @ExcelProperty(value = "角色权限") @NotBlank(message = "权限字符不能为空") @Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符") private String roleKey; /** * 角色排序 */ @ExcelProperty(value = "角色排序") @NotNull(message = "显示顺序不能为空") private Integer roleSort; /** * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ @ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class) @ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") private String dataScope; /** * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ private Boolean menuCheckStrictly; /** * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ private Boolean deptCheckStrictly; /** * 角色状态(0正常 1停用) */ @ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_normal_disable") private String status; /** * 删除标志(0代表存在 2代表删除) */ @TableLogic private String delFlag; /** * 备注 */ private String remark; /** * 用户是否存在此角色标识 默认不存在 */ @TableField(exist = false) private boolean flag = false; /** * 菜单组 */ @TableField(exist = false) private Long[] menuIds; /** * 部门组(数据权限) */ @TableField(exist = false) private Long[] deptIds; public SysRole(Long roleId) { this.roleId = roleId; } public boolean isAdmin() { return UserConstants.ADMIN_ID.equals(this.roleId); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/entity/SysUser.java ================================================ package top.flya.common.core.domain.entity; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import top.flya.common.annotation.Sensitive; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.BaseEntity; import top.flya.common.enums.SensitiveStrategy; import top.flya.common.xss.Xss; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import java.util.Date; import java.util.List; /** * 用户对象 sys_user * * @author Lion Li */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @TableName("sys_user") public class SysUser extends BaseEntity { /** * 用户ID */ @TableId(value = "user_id",type = IdType.AUTO) private Long userId; /** * 部门ID */ private Long deptId; /** * 用户账号 */ @Xss(message = "用户账号不能包含脚本字符") @NotBlank(message = "用户账号不能为空") @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") private String userName; /** * 用户昵称 */ @Xss(message = "用户昵称不能包含脚本字符") @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") private String nickName; /** * 用户类型(sys_user系统用户) */ private String userType; /** * 用户邮箱 */ @Sensitive(strategy = SensitiveStrategy.EMAIL) @Email(message = "邮箱格式不正确") @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") private String email; /** * 手机号码 */ @Sensitive(strategy = SensitiveStrategy.PHONE) private String phonenumber; /** * 用户性别 */ private String sex; /** * 用户头像 */ private String avatar; /** * 密码 */ @TableField( insertStrategy = FieldStrategy.NOT_EMPTY, updateStrategy = FieldStrategy.NOT_EMPTY, whereStrategy = FieldStrategy.NOT_EMPTY ) private String password; @JsonIgnore @JsonProperty public String getPassword() { return password; } /** * 帐号状态(0正常 1停用) */ private String status; /** * 删除标志(0代表存在 2代表删除) */ @TableLogic private String delFlag; /** * 最后登录IP */ private String loginIp; /** * 最后登录时间 */ private Date loginDate; /** * 备注 */ private String remark; /** * 部门对象 */ @TableField(exist = false) private SysDept dept; /** * 角色对象 */ @TableField(exist = false) private List roles; /** * 角色组 */ @TableField(exist = false) private Long[] roleIds; /** * 岗位组 */ @TableField(exist = false) private Long[] postIds; /** * 数据权限 当前角色ID */ @TableField(exist = false) private Long roleId; public SysUser(Long userId) { this.userId = userId; } public boolean isAdmin() { return UserConstants.ADMIN_ID.equals(this.userId); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/event/LogininforEvent.java ================================================ package top.flya.common.core.domain.event; import lombok.Data; import javax.servlet.http.HttpServletRequest; import java.io.Serializable; /** * 登录事件 * * @author Lion Li */ @Data public class LogininforEvent implements Serializable { private static final long serialVersionUID = 1L; /** * 用户账号 */ private String username; /** * 登录状态 0成功 1失败 */ private String status; /** * 提示消息 */ private String message; /** * 请求体 */ private HttpServletRequest request; /** * 其他参数 */ private Object[] args; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/event/OperLogEvent.java ================================================ package top.flya.common.core.domain.event; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * 操作日志事件 * * @author Lion Li */ @Data public class OperLogEvent implements Serializable { private static final long serialVersionUID = 1L; /** * 日志主键 */ private Long operId; /** * 操作模块 */ private String title; /** * 业务类型(0其它 1新增 2修改 3删除) */ private Integer businessType; /** * 业务类型数组 */ private Integer[] businessTypes; /** * 请求方法 */ private String method; /** * 请求方式 */ private String requestMethod; /** * 操作类别(0其它 1后台用户 2手机端用户) */ private Integer operatorType; /** * 操作人员 */ private String operName; /** * 部门名称 */ private String deptName; /** * 请求url */ private String operUrl; /** * 操作地址 */ private String operIp; /** * 操作地点 */ private String operLocation; /** * 请求参数 */ private String operParam; /** * 返回参数 */ private String jsonResult; /** * 操作状态(0正常 1异常) */ private Integer status; /** * 错误消息 */ private String errorMsg; /** * 操作时间 */ private Date operTime; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/model/EmailLoginBody.java ================================================ package top.flya.common.core.domain.model; import lombok.Data; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; /** * 短信登录对象 * * @author Lion Li */ @Data public class EmailLoginBody { /** * 邮箱 */ @NotBlank(message = "{user.email.not.blank}") @Email(message = "{user.email.not.valid}") private String email; /** * 邮箱code */ @NotBlank(message = "{email.code.not.blank}") private String emailCode; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/model/LoginBody.java ================================================ package top.flya.common.core.domain.model; import top.flya.common.constant.UserConstants; import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; /** * 用户登录对象 * * @author Lion Li */ @Data public class LoginBody { /** * 用户名 */ @NotBlank(message = "{user.username.not.blank}") @Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") private String username; /** * 用户密码 */ @NotBlank(message = "{user.password.not.blank}") @Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") private String password; /** * 验证码 */ private String code; /** * 唯一标识 */ private String uuid; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/model/LoginUser.java ================================================ package top.flya.common.core.domain.model; import top.flya.common.core.domain.dto.RoleDTO; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.List; import java.util.Set; /** * 登录用户身份权限 * * @author Lion Li */ @Data @NoArgsConstructor public class LoginUser implements Serializable { private static final long serialVersionUID = 1L; /** * 用户ID */ private Long userId; /** * 部门ID */ private Long deptId; /** * 部门名 */ private String deptName; /** * 用户唯一标识 */ private String token; /** * 用户类型 */ private String userType; /** * 登录时间 */ private Long loginTime; /** * 过期时间 */ private Long expireTime; /** * 登录IP地址 */ private String ipaddr; /** * 登录地点 */ private String loginLocation; /** * 浏览器类型 */ private String browser; /** * 操作系统 */ private String os; /** * 菜单权限 */ private Set menuPermission; /** * 角色权限 */ private Set rolePermission; /** * 用户名 */ private String username; /** * 角色对象 */ private List roles; /** * 数据权限 当前角色ID */ private Long roleId; /** * 获取登录id */ public String getLoginId() { if (userType == null) { throw new IllegalArgumentException("用户类型不能为空"); } if (userId == null) { throw new IllegalArgumentException("用户ID不能为空"); } return userType + ":" + userId; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/model/RegisterBody.java ================================================ package top.flya.common.core.domain.model; import lombok.Data; import lombok.EqualsAndHashCode; /** * 用户注册对象 * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) public class RegisterBody extends LoginBody { private String userType; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/model/SmsLoginBody.java ================================================ package top.flya.common.core.domain.model; import lombok.Data; import javax.validation.constraints.NotBlank; /** * 短信登录对象 * * @author Lion Li */ @Data public class SmsLoginBody { /** * 手机号 */ @NotBlank(message = "{user.phonenumber.not.blank}") private String phonenumber; /** * 短信code */ @NotBlank(message = "{sms.code.not.blank}") private String smsCode; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/domain/model/XcxLoginUser.java ================================================ package top.flya.common.core.domain.model; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * 小程序登录用户身份权限 * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor public class XcxLoginUser extends LoginUser { private static final long serialVersionUID = 1L; /** * openid */ private String openid; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/mapper/BaseMapperPlus.java ================================================ package top.flya.common.core.mapper; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.*; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.toolkit.Db; import top.flya.common.utils.BeanCopyUtils; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; /** * 自定义 Mapper 接口, 实现 自定义扩展 * * @param mapper 泛型 * @param table 泛型 * @param vo 泛型 * @author Lion Li * @since 2021-05-13 */ @SuppressWarnings("unchecked") public interface BaseMapperPlus extends BaseMapper { Log log = LogFactory.getLog(BaseMapperPlus.class); default Class currentVoClass() { return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2); } default Class currentModelClass() { return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1); } default Class currentMapperClass() { return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0); } default List selectList() { return this.selectList(new QueryWrapper<>()); } /** * 批量插入 */ default boolean insertBatch(Collection entityList) { return Db.saveBatch(entityList); } /** * 批量更新 */ default boolean updateBatchById(Collection entityList) { return Db.updateBatchById(entityList); } /** * 批量插入或更新 */ default boolean insertOrUpdateBatch(Collection entityList) { return Db.saveOrUpdateBatch(entityList); } /** * 批量插入(包含限制条数) */ default boolean insertBatch(Collection entityList, int batchSize) { return Db.saveBatch(entityList, batchSize); } /** * 批量更新(包含限制条数) */ default boolean updateBatchById(Collection entityList, int batchSize) { return Db.updateBatchById(entityList, batchSize); } /** * 批量插入或更新(包含限制条数) */ default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { return Db.saveOrUpdateBatch(entityList, batchSize); } /** * 插入或更新(包含限制条数) */ default boolean insertOrUpdate(T entity) { return Db.saveOrUpdate(entity); } default V selectVoById(Serializable id) { return selectVoById(id, this.currentVoClass()); } /** * 根据 ID 查询 */ default C selectVoById(Serializable id, Class voClass) { T obj = this.selectById(id); if (ObjectUtil.isNull(obj)) { return null; } return BeanCopyUtils.copy(obj, voClass); } default List selectVoBatchIds(Collection idList) { return selectVoBatchIds(idList, this.currentVoClass()); } /** * 查询(根据ID 批量查询) */ default List selectVoBatchIds(Collection idList, Class voClass) { List list = this.selectBatchIds(idList); if (CollUtil.isEmpty(list)) { return CollUtil.newArrayList(); } return BeanCopyUtils.copyList(list, voClass); } default List selectVoByMap(Map map) { return selectVoByMap(map, this.currentVoClass()); } /** * 查询(根据 columnMap 条件) */ default List selectVoByMap(Map map, Class voClass) { List list = this.selectByMap(map); if (CollUtil.isEmpty(list)) { return CollUtil.newArrayList(); } return BeanCopyUtils.copyList(list, voClass); } default V selectVoOne(Wrapper wrapper) { return selectVoOne(wrapper, this.currentVoClass()); } /** * 根据 entity 条件,查询一条记录 */ default C selectVoOne(Wrapper wrapper, Class voClass) { T obj = this.selectOne(wrapper); if (ObjectUtil.isNull(obj)) { return null; } return BeanCopyUtils.copy(obj, voClass); } default List selectVoList(Wrapper wrapper) { return selectVoList(wrapper, this.currentVoClass()); } /** * 根据 entity 条件,查询全部记录 */ default List selectVoList(Wrapper wrapper, Class voClass) { List list = this.selectList(wrapper); if (CollUtil.isEmpty(list)) { return CollUtil.newArrayList(); } return BeanCopyUtils.copyList(list, voClass); } default

> P selectVoPage(IPage page, Wrapper wrapper) { return selectVoPage(page, wrapper, this.currentVoClass()); } /** * 分页查询VO */ default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { IPage pageData = this.selectPage(page, wrapper); IPage voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()); if (CollUtil.isEmpty(pageData.getRecords())) { return (P) voPage; } voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass)); return (P) voPage; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/page/TableDataInfo.java ================================================ package top.flya.common.core.page; import cn.hutool.http.HttpStatus; import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.List; /** * 表格分页数据对象 * * @author Lion Li */ @Data @NoArgsConstructor public class TableDataInfo implements Serializable { private static final long serialVersionUID = 1L; /** * 总记录数 */ private long total; /** * 列表数据 */ private List rows; /** * 消息状态码 */ private int code; /** * 消息内容 */ private String msg; /** * 分页 * * @param list 列表数据 * @param total 总记录数 */ public TableDataInfo(List list, long total) { this.rows = list; this.total = total; } public static TableDataInfo build(IPage page) { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); rspData.setMsg("查询成功"); rspData.setRows(page.getRecords()); rspData.setTotal(page.getTotal()); return rspData; } public static TableDataInfo build(List list) { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); rspData.setMsg("查询成功"); rspData.setRows(list); rspData.setTotal(list.size()); return rspData; } public static TableDataInfo build() { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); rspData.setMsg("查询成功"); return rspData; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/service/ConfigService.java ================================================ package top.flya.common.core.service; /** * 通用 参数配置服务 * * @author Lion Li */ public interface ConfigService { /** * 根据参数 key 获取参数值 * * @param configKey 参数 key * @return 参数值 */ String getConfigValue(String configKey); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/service/DeptService.java ================================================ package top.flya.common.core.service; /** * 通用 部门服务 * * @author Lion Li */ public interface DeptService { /** * 通过部门ID查询部门名称 * * @param deptIds 部门ID串逗号分隔 * @return 部门名称串逗号分隔 */ String selectDeptNameByIds(String deptIds); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/service/DictService.java ================================================ package top.flya.common.core.service; /** * 通用 字典服务 * * @author Lion Li */ public interface DictService { /** * 分隔符 */ String SEPARATOR = ","; /** * 根据字典类型和字典值获取字典标签 * * @param dictType 字典类型 * @param dictValue 字典值 * @return 字典标签 */ default String getDictLabel(String dictType, String dictValue) { return getDictLabel(dictType, dictValue, SEPARATOR); } /** * 根据字典类型和字典标签获取字典值 * * @param dictType 字典类型 * @param dictLabel 字典标签 * @return 字典值 */ default String getDictValue(String dictType, String dictLabel) { return getDictValue(dictType, dictLabel, SEPARATOR); } /** * 根据字典类型和字典值获取字典标签 * * @param dictType 字典类型 * @param dictValue 字典值 * @param separator 分隔符 * @return 字典标签 */ String getDictLabel(String dictType, String dictValue, String separator); /** * 根据字典类型和字典标签获取字典值 * * @param dictType 字典类型 * @param dictLabel 字典标签 * @param separator 分隔符 * @return 字典值 */ String getDictValue(String dictType, String dictLabel, String separator); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/service/OssService.java ================================================ package top.flya.common.core.service; /** * 通用 OSS服务 * * @author Lion Li */ public interface OssService { /** * 通过ossId查询对应的url * * @param ossIds ossId串逗号分隔 * @return url串逗号分隔 */ String selectUrlByIds(String ossIds); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/service/SensitiveService.java ================================================ package top.flya.common.core.service; /** * 脱敏服务 * 默认管理员不过滤 * 需自行根据业务重写实现 * * @author Lion Li * @version 3.6.0 */ public interface SensitiveService { /** * 是否脱敏 */ boolean isSensitive(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/service/UserService.java ================================================ package top.flya.common.core.service; /** * 通用 用户服务 * * @author Lion Li */ public interface UserService { /** * 通过用户ID查询用户账户 * * @param userId 用户ID * @return 用户账户 */ String selectUserNameById(Long userId); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/validate/AddGroup.java ================================================ package top.flya.common.core.validate; /** * 校验分组 add * * @author Lion Li */ public interface AddGroup { } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/validate/EditGroup.java ================================================ package top.flya.common.core.validate; /** * 校验分组 edit * * @author Lion Li */ public interface EditGroup { } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/core/validate/QueryGroup.java ================================================ package top.flya.common.core.validate; /** * 校验分组 query * * @author Lion Li */ public interface QueryGroup { } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/EncryptContext.java ================================================ package top.flya.common.encrypt; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import lombok.Data; /** * 加密上下文 用于encryptor传递必要的参数。 * * @author 老马 * @version 4.6.0 */ @Data public class EncryptContext { /** * 默认算法 */ private AlgorithmType algorithm; /** * 安全秘钥 */ private String password; /** * 公钥 */ private String publicKey; /** * 私钥 */ private String privateKey; /** * 编码方式,base64/hex */ private EncodeType encode; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/IEncryptor.java ================================================ package top.flya.common.encrypt; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; /** * 加解者 * * @author 老马 * @version 4.6.0 */ public interface IEncryptor { /** * 获得当前算法 */ AlgorithmType algorithm(); /** * 加密 * * @param value 待加密字符串 * @param encodeType 加密后的编码格式 * @return 加密后的字符串 */ String encrypt(String value, EncodeType encodeType); /** * 解密 * * @param value 待加密字符串 * @return 解密后的字符串 */ String decrypt(String value); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/AbstractEncryptor.java ================================================ package top.flya.common.encrypt.encryptor; import top.flya.common.encrypt.EncryptContext; import top.flya.common.encrypt.IEncryptor; /** * 所有加密执行者的基类 * * @author 老马 * @version 4.6.0 */ public abstract class AbstractEncryptor implements IEncryptor { public AbstractEncryptor(EncryptContext context) { // 用户配置校验与配置注入 } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/AesEncryptor.java ================================================ package top.flya.common.encrypt.encryptor; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.AES; import top.flya.common.encrypt.EncryptContext; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import java.nio.charset.StandardCharsets; /** * AES算法实现 * * @author 老马 * @version 4.6.0 */ public class AesEncryptor extends AbstractEncryptor { private final AES aes; public AesEncryptor(EncryptContext context) { super(context); String password = context.getPassword(); if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("AES没有获得秘钥信息"); } // aes算法的秘钥要求是16位、24位、32位 int[] array = {16, 24, 32}; if (!ArrayUtil.contains(array, password.length())) { throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位,实际为" + password.length() + "位"); } aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8)); } /** * 获得当前算法 */ @Override public AlgorithmType algorithm() { return AlgorithmType.AES; } /** * 加密 * * @param value 待加密字符串 * @param encodeType 加密后的编码格式 */ @Override public String encrypt(String value, EncodeType encodeType) { if (encodeType == EncodeType.HEX) { return aes.encryptHex(value); } else { return aes.encryptBase64(value); } } /** * 解密 * * @param value 待加密字符串 */ @Override public String decrypt(String value) { return this.aes.decryptStr(value); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/Base64Encryptor.java ================================================ package top.flya.common.encrypt.encryptor; import cn.hutool.core.codec.Base64; import top.flya.common.encrypt.EncryptContext; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; /** * Base64算法实现 * * @author 老马 * @version 4.6.0 */ public class Base64Encryptor extends AbstractEncryptor { public Base64Encryptor(EncryptContext context) { super(context); } /** * 获得当前算法 */ @Override public AlgorithmType algorithm() { return AlgorithmType.BASE64; } /** * 加密 * * @param value 待加密字符串 * @param encodeType 加密后的编码格式 */ @Override public String encrypt(String value, EncodeType encodeType) { return Base64.encode(value); } /** * 解密 * * @param value 待加密字符串 */ @Override public String decrypt(String value) { return Base64.decodeStr(value); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/RsaEncryptor.java ================================================ package top.flya.common.encrypt.encryptor; import cn.hutool.core.codec.Base64; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import top.flya.common.encrypt.EncryptContext; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import top.flya.common.utils.StringUtils; /** * RSA算法实现 * * @author 老马 * @version 4.6.0 */ public class RsaEncryptor extends AbstractEncryptor { private final RSA rsa; public RsaEncryptor(EncryptContext context) { super(context); String privateKey = context.getPrivateKey(); String publicKey = context.getPublicKey(); if (StringUtils.isAnyEmpty(privateKey, publicKey)) { throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。"); } this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey)); } /** * 获得当前算法 */ @Override public AlgorithmType algorithm() { return AlgorithmType.RSA; } /** * 加密 * * @param value 待加密字符串 * @param encodeType 加密后的编码格式 */ @Override public String encrypt(String value, EncodeType encodeType) { if (encodeType == EncodeType.HEX) { return rsa.encryptHex(value, KeyType.PublicKey); } else { return rsa.encryptBase64(value, KeyType.PublicKey); } } /** * 解密 * * @param value 待加密字符串 */ @Override public String decrypt(String value) { return this.rsa.decryptStr(value, KeyType.PrivateKey); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/Sm2Encryptor.java ================================================ package top.flya.common.encrypt.encryptor; import cn.hutool.core.codec.Base64; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.SM2; import top.flya.common.encrypt.EncryptContext; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import top.flya.common.utils.StringUtils; /** * sm2算法实现 * * @author 老马 * @version 4.6.0 */ public class Sm2Encryptor extends AbstractEncryptor { private final SM2 sm2; public Sm2Encryptor(EncryptContext context) { super(context); String privateKey = context.getPrivateKey(); String publicKey = context.getPublicKey(); if (StringUtils.isAnyEmpty(privateKey, publicKey)) { throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。"); } this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey)); } /** * 获得当前算法 */ @Override public AlgorithmType algorithm() { return AlgorithmType.SM2; } /** * 加密 * * @param value 待加密字符串 * @param encodeType 加密后的编码格式 */ @Override public String encrypt(String value, EncodeType encodeType) { if (encodeType == EncodeType.HEX) { return sm2.encryptHex(value, KeyType.PublicKey); } else { return sm2.encryptBase64(value, KeyType.PublicKey); } } /** * 解密 * * @param value 待加密字符串 */ @Override public String decrypt(String value) { return this.sm2.decryptStr(value, KeyType.PrivateKey); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/encrypt/encryptor/Sm4Encryptor.java ================================================ package top.flya.common.encrypt.encryptor; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.symmetric.SM4; import top.flya.common.encrypt.EncryptContext; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import java.nio.charset.StandardCharsets; /** * sm4算法实现 * * @author 老马 * @version 4.6.0 */ public class Sm4Encryptor extends AbstractEncryptor { private final SM4 sm4; public Sm4Encryptor(EncryptContext context) { super(context); String password = context.getPassword(); if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("SM4没有获得秘钥信息"); } // sm4算法的秘钥要求是16位长度 if (16 != password.length()) { throw new IllegalArgumentException("SM4秘钥长度应该为16位,实际为" + password.length() + "位"); } this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)); } /** * 获得当前算法 */ @Override public AlgorithmType algorithm() { return AlgorithmType.SM4; } /** * 加密 * * @param value 待加密字符串 * @param encodeType 加密后的编码格式 */ @Override public String encrypt(String value, EncodeType encodeType) { if (encodeType == EncodeType.HEX) { return sm4.encryptHex(value); } else { return sm4.encryptBase64(value); } } /** * 解密 * * @param value 待加密字符串 */ @Override public String decrypt(String value) { return this.sm4.decryptStr(value); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/AlgorithmType.java ================================================ package top.flya.common.enums; import top.flya.common.encrypt.encryptor.*; import lombok.AllArgsConstructor; import lombok.Getter; import top.flya.common.encrypt.encryptor.*; /** * 算法名称 * * @author 老马 * @version 4.6.0 */ @Getter @AllArgsConstructor public enum AlgorithmType { /** * 默认走yml配置 */ DEFAULT(null), /** * base64 */ BASE64(Base64Encryptor.class), /** * aes */ AES(AesEncryptor.class), /** * rsa */ RSA(RsaEncryptor.class), /** * sm2 */ SM2(Sm2Encryptor.class), /** * sm4 */ SM4(Sm4Encryptor.class); private final Class clazz; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/BusinessStatus.java ================================================ package top.flya.common.enums; /** * 操作状态 * * @author ruoyi */ public enum BusinessStatus { /** * 成功 */ SUCCESS, /** * 失败 */ FAIL, } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/BusinessType.java ================================================ package top.flya.common.enums; /** * 业务操作类型 * * @author ruoyi */ public enum BusinessType { /** * 其它 */ OTHER, /** * 新增 */ INSERT, /** * 修改 */ UPDATE, /** * 删除 */ DELETE, /** * 授权 */ GRANT, /** * 导出 */ EXPORT, /** * 导入 */ IMPORT, /** * 强退 */ FORCE, /** * 生成代码 */ GENCODE, /** * 清空数据 */ CLEAN, } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/CaptchaCategory.java ================================================ package top.flya.common.enums; import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.CircleCaptcha; import cn.hutool.captcha.LineCaptcha; import cn.hutool.captcha.ShearCaptcha; import lombok.AllArgsConstructor; import lombok.Getter; /** * 验证码类别 * * @author Lion Li */ @Getter @AllArgsConstructor public enum CaptchaCategory { /** * 线段干扰 */ LINE(LineCaptcha.class), /** * 圆圈干扰 */ CIRCLE(CircleCaptcha.class), /** * 扭曲干扰 */ SHEAR(ShearCaptcha.class); private final Class clazz; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/CaptchaType.java ================================================ package top.flya.common.enums; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.captcha.generator.RandomGenerator; import top.flya.common.captcha.UnsignedMathGenerator; import lombok.AllArgsConstructor; import lombok.Getter; /** * 验证码类型 * * @author Lion Li */ @Getter @AllArgsConstructor public enum CaptchaType { /** * 数字 */ MATH(UnsignedMathGenerator.class), /** * 字符 */ CHAR(RandomGenerator.class); private final Class clazz; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/DataBaseType.java ================================================ package top.flya.common.enums; import top.flya.common.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Getter; /** * 数据库类型 * * @author Lion Li */ @Getter @AllArgsConstructor public enum DataBaseType { /** * MySQL */ MY_SQL("MySQL"), /** * Oracle */ ORACLE("Oracle"), /** * PostgreSQL */ POSTGRE_SQL("PostgreSQL"), /** * SQL Server */ SQL_SERVER("Microsoft SQL Server"); private final String type; public static DataBaseType find(String databaseProductName) { if (StringUtils.isBlank(databaseProductName)) { return null; } for (DataBaseType type : values()) { if (type.getType().equals(databaseProductName)) { return type; } } return null; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/DataScopeType.java ================================================ package top.flya.common.enums; import top.flya.common.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Getter; import top.flya.common.helper.DataPermissionHelper; /** * 数据权限类型 *

* 语法支持 spel 模板表达式 *

* 内置数据 user 当前用户 内容参考 LoginUser * 如需扩展数据 可使用 {@link DataPermissionHelper} 操作 * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService * 如需扩展更多自定义服务 可以参考 sdss 自行编写 * * @author Lion Li * @version 3.5.0 */ @Getter @AllArgsConstructor public enum DataScopeType { /** * 全部数据权限 */ ALL("1", "", ""), /** * 自定数据权限 */ CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""), /** * 部门数据权限 */ DEPT("3", " #{#deptName} = #{#user.deptId} ", ""), /** * 部门及以下数据权限 */ DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""), /** * 仅本人数据权限 */ SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "); private final String code; /** * 语法 采用 spel 模板表达式 */ private final String sqlTemplate; /** * 不满足 sqlTemplate 则填充 */ private final String elseSql; public static DataScopeType findCode(String code) { if (StringUtils.isBlank(code)) { return null; } for (DataScopeType type : values()) { if (type.getCode().equals(code)) { return type; } } return null; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/DeviceType.java ================================================ package top.flya.common.enums; import lombok.AllArgsConstructor; import lombok.Getter; /** * 设备类型 * 针对一套 用户体系 * * @author Lion Li */ @Getter @AllArgsConstructor public enum DeviceType { /** * pc端 */ PC("pc"), /** * app端 */ APP("app"), /** * 小程序端 */ XCX("xcx"); private final String device; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/EncodeType.java ================================================ package top.flya.common.enums; /** * 编码类型 * * @author 老马 * @version 4.6.0 */ public enum EncodeType { /** * 默认使用yml配置 */ DEFAULT, /** * base64编码 */ BASE64, /** * 16进制编码 */ HEX; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/HttpMethod.java ================================================ package top.flya.common.enums; import org.springframework.lang.Nullable; import java.util.HashMap; import java.util.Map; /** * 请求方式 * * @author ruoyi */ public enum HttpMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; private static final Map 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)); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/LimitType.java ================================================ package top.flya.common.enums; /** * 限流类型 * * @author ruoyi */ public enum LimitType { /** * 默认策略全局限流 */ DEFAULT, /** * 根据请求者IP进行限流 */ IP, /** * 实例限流(集群多后端实例) */ CLUSTER } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/LoginType.java ================================================ package top.flya.common.enums; import lombok.AllArgsConstructor; import lombok.Getter; /** * 登录类型 * * @author Lion Li */ @Getter @AllArgsConstructor public enum LoginType { /** * 密码登录 */ PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"), /** * 短信登录 */ SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), /** * 邮箱登录 */ EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), /** * 小程序登录 */ XCX("", ""); /** * 登录重试超出限制提示 */ final String retryLimitExceed; /** * 登录重试限制计数提示 */ final String retryLimitCount; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/OperatorType.java ================================================ package top.flya.common.enums; /** * 操作人类别 * * @author ruoyi */ public enum OperatorType { /** * 其它 */ OTHER, /** * 后台用户 */ MANAGE, /** * 手机端用户 */ MOBILE } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/SensitiveStrategy.java ================================================ package top.flya.common.enums; import cn.hutool.core.util.DesensitizedUtil; import lombok.AllArgsConstructor; import java.util.function.Function; /** * 脱敏策略 * * @author Yjoioooo * @version 3.6.0 */ @AllArgsConstructor public enum SensitiveStrategy { /** * 身份证脱敏 */ ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)), /** * 手机号脱敏 */ PHONE(DesensitizedUtil::mobilePhone), /** * 地址脱敏 */ ADDRESS(s -> DesensitizedUtil.address(s, 8)), /** * 邮箱脱敏 */ EMAIL(DesensitizedUtil::email), /** * 银行卡 */ BANK_CARD(DesensitizedUtil::bankCard); //可自行添加其他脱敏策略 private final Function desensitizer; public Function desensitizer() { return desensitizer; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/UserStatus.java ================================================ package top.flya.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; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/enums/UserType.java ================================================ package top.flya.common.enums; import top.flya.common.utils.StringUtils; import lombok.AllArgsConstructor; import lombok.Getter; /** * 设备类型 * 针对多套 用户体系 * * @author Lion Li */ @Getter @AllArgsConstructor public enum UserType { /** * pc端 */ SYS_USER("sys_user"), /** * app端 */ APP_USER("app_user"), WX_USER("微信小程序用户"); private final String userType; public static UserType getUserType(String str) { for (UserType value : values()) { if (StringUtils.contains(str, value.getUserType())) { return value; } } throw new RuntimeException("'UserType' not found By " + str); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/excel/CellMergeStrategy.java ================================================ package top.flya.common.excel; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.merge.AbstractMergeStrategy; import top.flya.common.annotation.CellMerge; import lombok.AllArgsConstructor; import lombok.Data; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 列值重复合并策略 * * @author Lion Li */ @AllArgsConstructor @Slf4j public class CellMergeStrategy extends AbstractMergeStrategy { private List list; private boolean hasTitle; @Override protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { List cellList = handle(list, hasTitle); // judge the list is not null if (CollectionUtils.isNotEmpty(cellList)) { // the judge is necessary if (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) { for (CellRangeAddress item : cellList) { sheet.addMergedRegion(item); } } } } @SneakyThrows private static List handle(List list, boolean hasTitle) { List cellList = new ArrayList<>(); if (CollectionUtils.isEmpty(list)) { return cellList; } Class clazz = list.get(0).getClass(); Field[] fields = clazz.getDeclaredFields(); // 有注解的字段 List mergeFields = new ArrayList<>(); List mergeFieldsIndex = new ArrayList<>(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; if (field.isAnnotationPresent(CellMerge.class)) { CellMerge cm = field.getAnnotation(CellMerge.class); mergeFields.add(field); mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index()); } } // 行合并开始下标 int rowIndex = hasTitle ? 1 : 0; Map map = new HashMap<>(); // 生成两两合并单元格 for (int i = 0; i < list.size(); i++) { for (int j = 0; j < mergeFields.size(); j++) { Field field = mergeFields.get(j); String name = field.getName(); String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); Method readMethod = clazz.getMethod(methodName); Object val = readMethod.invoke(list.get(i)); int colNum = mergeFieldsIndex.get(j); if (!map.containsKey(field)) { map.put(field, new RepeatCell(val, i)); } else { RepeatCell repeatCell = map.get(field); Object cellValue = repeatCell.getValue(); if (cellValue == null || "".equals(cellValue)) { // 空值跳过不合并 continue; } if (!cellValue.equals(val)) { if (i - repeatCell.getCurrent() > 1) { cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); } map.put(field, new RepeatCell(val, i)); } else if (i == list.size() - 1) { if (i > repeatCell.getCurrent()) { cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); } } } } } return cellList; } @Data @AllArgsConstructor static class RepeatCell { private Object value; private int current; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/excel/DefaultExcelListener.java ================================================ package top.flya.common.excel; import cn.hutool.core.util.StrUtil; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelDataConvertException; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.ValidatorUtils; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.Map; import java.util.Set; /** * Excel 导入监听 * * @author Yjoioooo * @author Lion Li */ @Slf4j @NoArgsConstructor public class DefaultExcelListener extends AnalysisEventListener implements ExcelListener { /** * 是否Validator检验,默认为是 */ private Boolean isValidate = Boolean.TRUE; /** * excel 表头数据 */ private Map headMap; /** * 导入回执 */ private ExcelResult excelResult; public DefaultExcelListener(boolean isValidate) { this.excelResult = new DefaultExcelResult<>(); this.isValidate = isValidate; } /** * 处理异常 * * @param exception ExcelDataConvertException * @param context Excel 上下文 */ @Override public void onException(Exception exception, AnalysisContext context) throws Exception { String errMsg = null; if (exception instanceof ExcelDataConvertException) { // 如果是某一个单元格的转换异常 能获取到具体行号 ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception; Integer rowIndex = excelDataConvertException.getRowIndex(); Integer columnIndex = excelDataConvertException.getColumnIndex(); errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常
", rowIndex + 1, columnIndex + 1, headMap.get(columnIndex)); if (log.isDebugEnabled()) { log.error(errMsg); } } if (exception instanceof ConstraintViolationException) { ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception; Set> constraintViolations = constraintViolationException.getConstraintViolations(); String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", "); errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg); if (log.isDebugEnabled()) { log.error(errMsg); } } excelResult.getErrorList().add(errMsg); throw new ExcelAnalysisException(errMsg); } @Override public void invokeHeadMap(Map headMap, AnalysisContext context) { this.headMap = headMap; log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap)); } @Override public void invoke(T data, AnalysisContext context) { if (isValidate) { ValidatorUtils.validate(data); } excelResult.getList().add(data); } @Override public void doAfterAllAnalysed(AnalysisContext context) { log.debug("所有数据解析完成!"); } @Override public ExcelResult getExcelResult() { return excelResult; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/excel/DefaultExcelResult.java ================================================ package top.flya.common.excel; import cn.hutool.core.util.StrUtil; import lombok.Setter; import java.util.ArrayList; import java.util.List; /** * 默认excel返回对象 * * @author Yjoioooo * @author Lion Li */ public class DefaultExcelResult implements ExcelResult { /** * 数据对象list */ @Setter private List list; /** * 错误信息列表 */ @Setter private List errorList; public DefaultExcelResult() { this.list = new ArrayList<>(); this.errorList = new ArrayList<>(); } public DefaultExcelResult(List list, List errorList) { this.list = list; this.errorList = errorList; } public DefaultExcelResult(ExcelResult excelResult) { this.list = excelResult.getList(); this.errorList = excelResult.getErrorList(); } @Override public List getList() { return list; } @Override public List getErrorList() { return errorList; } /** * 获取导入回执 * * @return 导入回执 */ @Override public String getAnalysis() { int successCount = list.size(); int errorCount = errorList.size(); if (successCount == 0) { return "读取失败,未解析到数据"; } else { if (errorCount == 0) { return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount); } else { return ""; } } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/excel/ExcelListener.java ================================================ package top.flya.common.excel; import com.alibaba.excel.read.listener.ReadListener; /** * Excel 导入监听 * * @author Lion Li */ public interface ExcelListener extends ReadListener { ExcelResult getExcelResult(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/excel/ExcelResult.java ================================================ package top.flya.common.excel; import java.util.List; /** * excel返回对象 * * @author Lion Li */ public interface ExcelResult { /** * 对象列表 */ List getList(); /** * 错误列表 */ List getErrorList(); /** * 导入回执 */ String getAnalysis(); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/DemoModeException.java ================================================ package top.flya.common.exception; /** * 演示模式异常 * * @author ruoyi */ public class DemoModeException extends RuntimeException { private static final long serialVersionUID = 1L; public DemoModeException() { } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/GlobalException.java ================================================ package top.flya.common.exception; /** * 全局异常 * * @author ruoyi */ public class GlobalException extends RuntimeException { private static final long serialVersionUID = 1L; /** * 错误提示 */ private String message; /** * 错误明细,内部调试错误 *

* 和 {@link CommonResult#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; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/ServiceException.java ================================================ package top.flya.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; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/UtilException.java ================================================ package top.flya.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); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/base/BaseException.java ================================================ package top.flya.common.exception.base; import top.flya.common.utils.MessageUtils; import top.flya.common.utils.StringUtils; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * 基础异常 * * @author ruoyi */ @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor 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; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/file/FileException.java ================================================ package top.flya.common.exception.file; import top.flya.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); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/file/FileNameLengthLimitExceededException.java ================================================ package top.flya.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}); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/file/FileSizeLimitExceededException.java ================================================ package top.flya.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}); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/user/CaptchaException.java ================================================ package top.flya.common.exception.user; /** * 验证码错误异常类 * * @author ruoyi */ public class CaptchaException extends UserException { private static final long serialVersionUID = 1L; public CaptchaException() { super("user.jcaptcha.error"); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/user/CaptchaExpireException.java ================================================ package top.flya.common.exception.user; /** * 验证码失效异常类 * * @author ruoyi */ public class CaptchaExpireException extends UserException { private static final long serialVersionUID = 1L; public CaptchaExpireException() { super("user.jcaptcha.expire"); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/user/UserException.java ================================================ package top.flya.common.exception.user; import top.flya.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); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/user/UserPasswordNotMatchException.java ================================================ package top.flya.common.exception.user; /** * 用户密码不正确或不符合规范异常类 * * @author ruoyi */ public class UserPasswordNotMatchException extends UserException { private static final long serialVersionUID = 1L; public UserPasswordNotMatchException() { super("user.password.not.match"); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/exception/user/UserPasswordRetryLimitExceedException.java ================================================ package top.flya.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", retryLimitCount, lockTime); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/filter/RepeatableFilter.java ================================================ package top.flya.common.filter; import top.flya.common.utils.StringUtils; import org.springframework.http.MediaType; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 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() { } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/filter/RepeatedlyRequestWrapper.java ================================================ package top.flya.common.filter; import cn.hutool.core.io.IoUtil; import top.flya.common.constant.Constants; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; /** * 构建可重复读取inputStream的request * * @author 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 = IoUtil.readBytes(request.getInputStream(), false); } @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) { } }; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/filter/XssFilter.java ================================================ package top.flya.common.filter; import top.flya.common.enums.HttpMethod; import top.flya.common.utils.StringUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 防止XSS攻击的过滤器 * * @author ruoyi */ public class XssFilter implements Filter { /** * 排除链接 */ public List excludes = new ArrayList<>(); @Override public void init(FilterConfig filterConfig) throws ServletException { String tempExcludes = filterConfig.getInitParameter("excludes"); if (StringUtils.isNotEmpty(tempExcludes)) { String[] url = tempExcludes.split(StringUtils.SEPARATOR); for (int i = 0; url != null && i < url.length; i++) { excludes.add(url[i]); } } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (handleExcludeURL(req, resp)) { chain.doFilter(request, response); return; } XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); chain.doFilter(xssRequest, response); } private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) { String url = request.getServletPath(); String method = request.getMethod(); // GET DELETE 不过滤 if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) { return true; } return StringUtils.matches(url, excludes); } @Override public void destroy() { } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/filter/XssHttpServletRequestWrapper.java ================================================ package top.flya.common.filter; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HtmlUtil; import top.flya.common.utils.StringUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; /** * 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] = HtmlUtil.cleanHtmlTag(values[i]).trim(); } return escapesValues; } return super.getParameterValues(name); } @Override public ServletInputStream getInputStream() throws IOException { // 非json类型,直接返回 if (!isJsonRequest()) { return super.getInputStream(); } // 为空,直接返回 String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8); if (StringUtils.isEmpty(json)) { return super.getInputStream(); } // xss过滤 json = HtmlUtil.cleanHtmlTag(json).trim(); byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8); final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes); return new ServletInputStream() { @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public int available() throws IOException { return jsonBytes.length; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return bis.read(); } }; } /** * 是否是Json请求 */ public boolean isJsonRequest() { String header = super.getHeader(HttpHeaders.CONTENT_TYPE); return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/helper/DataBaseHelper.java ================================================ package top.flya.common.helper; import cn.hutool.core.convert.Convert; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import top.flya.common.enums.DataBaseType; import top.flya.common.exception.ServiceException; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; /** * 数据库助手 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class DataBaseHelper { private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class); /** * 获取当前数据库类型 */ public static DataBaseType getDataBaseType() { DataSource dataSource = DS.determineDataSource(); try (Connection conn = dataSource.getConnection()) { DatabaseMetaData metaData = conn.getMetaData(); String databaseProductName = metaData.getDatabaseProductName(); return DataBaseType.find(databaseProductName); } catch (SQLException e) { throw new ServiceException(e.getMessage()); } } public static boolean isMySql() { return DataBaseType.MY_SQL == getDataBaseType(); } public static boolean isOracle() { return DataBaseType.ORACLE == getDataBaseType(); } public static boolean isPostgerSql() { return DataBaseType.POSTGRE_SQL == getDataBaseType(); } public static boolean isSqlServer() { return DataBaseType.SQL_SERVER == getDataBaseType(); } public static String findInSet(Object var1, String var2) { DataBaseType dataBasyType = getDataBaseType(); String var = Convert.toStr(var1); if (dataBasyType == DataBaseType.SQL_SERVER) { // charindex(',100,' , ',0,100,101,') <> 0 return "charindex('," + var + ",' , ','+" + var2 + "+',') <> 0"; } else if (dataBasyType == DataBaseType.POSTGRE_SQL) { // (select position(',100,' in ',0,100,101,')) <> 0 return "(select position('," + var + ",' in ','||" + var2 + "||',')) <> 0"; } else if (dataBasyType == DataBaseType.ORACLE) { // instr(',0,100,101,' , ',100,') <> 0 return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0"; } // find_in_set('100' , '0,100,101') return "find_in_set('" + var + "' , " + var2 + ") <> 0"; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/helper/DataPermissionHelper.java ================================================ package top.flya.common.helper; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.model.SaStorage; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; /** * 数据权限助手 * * @author Lion Li * @version 3.5.0 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) @SuppressWarnings("unchecked cast") public class DataPermissionHelper { private static final String DATA_PERMISSION_KEY = "data:permission"; public static T getVariable(String key) { Map context = getContext(); return (T) context.get(key); } public static void setVariable(String key, Object value) { Map context = getContext(); context.put(key, value); } public static Map getContext() { SaStorage saStorage = SaHolder.getStorage(); Object attribute = saStorage.get(DATA_PERMISSION_KEY); if (ObjectUtil.isNull(attribute)) { saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); attribute = saStorage.get(DATA_PERMISSION_KEY); } if (attribute instanceof Map) { return (Map) attribute; } throw new NullPointerException("data permission context type exception"); } /** * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) */ public static void enableIgnore() { InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); } /** * 关闭忽略数据权限 */ public static void disableIgnore() { InterceptorIgnoreHelper.clearIgnoreStrategy(); } /** * 在忽略数据权限中执行 * * @param handle 处理执行方法 */ public static void ignore(Runnable handle) { enableIgnore(); try { handle.run(); } finally { disableIgnore(); } } /** * 在忽略数据权限中执行 * * @param handle 处理执行方法 */ public static T ignore(Supplier handle) { enableIgnore(); try { return handle.get(); } finally { disableIgnore(); } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/helper/LoginHelper.java ================================================ package top.flya.common.helper; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.enums.DeviceType; import top.flya.common.enums.UserType; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * 登录鉴权助手 *

* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios * 可以组成 用户类型与设备类型多对多的 权限灵活控制 *

* 多用户体系 针对 多种用户类型 但权限控制不一致 * 可以组成 多用户类型表与多设备类型 分别控制权限 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class LoginHelper { public static final String LOGIN_USER_KEY = "loginUser"; public static final String USER_KEY = "userId"; /** * 登录系统 * * @param loginUser 登录用户信息 */ public static void login(LoginUser loginUser) { loginByDevice(loginUser, null); } /** * 登录系统 基于 设备类型 * 针对相同用户体系不同设备 * * @param loginUser 登录用户信息 */ public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) { SaStorage storage = SaHolder.getStorage(); storage.set(LOGIN_USER_KEY, loginUser); storage.set(USER_KEY, loginUser.getUserId()); SaLoginModel model = new SaLoginModel(); if (ObjectUtil.isNotNull(deviceType)) { model.setDevice(deviceType.getDevice()); } StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId())); StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser); } /** * 获取用户(多级缓存) */ public static LoginUser getLoginUser() { LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY); if (loginUser != null) { return loginUser; } loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY); SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); return loginUser; } /** * 获取用户基于token */ public static LoginUser getLoginUser(String token) { return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY); } /** * 获取用户id */ public static Long getUserId() { Long userId; try { userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY)); if (ObjectUtil.isNull(userId)) { userId = Convert.toLong(StpUtil.getExtra(USER_KEY)); SaHolder.getStorage().set(USER_KEY, userId); } } catch (Exception e) { return null; } return userId; } /** * 获取部门ID */ public static Long getDeptId() { return getLoginUser().getDeptId(); } /** * 获取用户账户 */ public static String getUsername() { return getLoginUser().getUsername(); } /** * 获取用户类型 */ public static UserType getUserType() { String loginId = StpUtil.getLoginIdAsString(); return UserType.getUserType(loginId); } /** * 是否为管理员 * * @param userId 用户ID * @return 结果 */ public static boolean isAdmin(Long userId) { return UserConstants.ADMIN_ID.equals(userId); } public static boolean isAdmin() { return isAdmin(getUserId()); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/jackson/DictDataJsonSerializer.java ================================================ package top.flya.common.jackson; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; 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 top.flya.common.annotation.DictDataMapper; import top.flya.common.core.service.DictService; import top.flya.common.utils.StringUtils; import top.flya.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import java.io.IOException; import java.util.Objects; /** * 字典数据json序列化工具 * * @author itino * @deprecated 建议使用通用翻译注解 */ @Deprecated @Slf4j public class DictDataJsonSerializer extends JsonSerializer implements ContextualSerializer { private String dictType; @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { try { DictService dictService = SpringUtils.getBean(DictService.class); if (ObjectUtil.isNotNull(dictService)) { String label = dictService.getDictLabel(dictType, value); gen.writeString(StringUtils.isNotBlank(label) ? label : value); } else { gen.writeString(value); } } catch (BeansException e) { log.error("字典数据未查到, 采用默认处理 => {}", e.getMessage()); gen.writeString(value); } } @Override public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { DictDataMapper anno = property.getAnnotation(DictDataMapper.class); if (Objects.nonNull(anno) && StrUtil.isNotBlank(anno.dictType())) { this.dictType = anno.dictType(); return this; } return prov.findValueSerializer(property.getType(), property); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/jackson/SensitiveJsonSerializer.java ================================================ package top.flya.common.jackson; import cn.hutool.core.util.ObjectUtil; 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 top.flya.common.annotation.Sensitive; import top.flya.common.core.service.SensitiveService; import top.flya.common.enums.SensitiveStrategy; import top.flya.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import java.io.IOException; import java.util.Objects; /** * 数据脱敏json序列化工具 * * @author Yjoioooo */ @Slf4j public class SensitiveJsonSerializer extends JsonSerializer implements ContextualSerializer { private SensitiveStrategy strategy; @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { try { SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class); if (ObjectUtil.isNotNull(sensitiveService) && sensitiveService.isSensitive()) { gen.writeString(strategy.desensitizer().apply(value)); } else { gen.writeString(value); } } catch (BeansException e) { log.error("脱敏实现不存在, 采用默认处理 => {}", e.getMessage()); 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.strategy = annotation.strategy(); return this; } return prov.findValueSerializer(property.getType(), property); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/translation/TranslationInterface.java ================================================ package top.flya.common.translation; import top.flya.common.annotation.TranslationType; /** * 翻译接口 (实现类需标注 {@link TranslationType} 注解标明翻译类型) * * @author Lion Li */ public interface TranslationInterface { /** * 翻译 * * @param key 需要被翻译的键(不为空) * @return 返回键对应的值 */ T translation(Object key, String other); } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/translation/handler/TranslationBeanSerializerModifier.java ================================================ package top.flya.common.translation.handler; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; import java.util.List; /** * Bean 序列化修改器 解决 Null 被单独处理问题 * * @author Lion Li */ public class TranslationBeanSerializerModifier extends BeanSerializerModifier { @Override public List changeProperties(SerializationConfig config, BeanDescription beanDesc, List beanProperties) { for (BeanPropertyWriter writer : beanProperties) { // 如果序列化器为 TranslationHandler 的话 将 Null 值也交给他处理 if (writer.getSerializer() instanceof TranslationHandler) { writer.assignNullSerializer(writer.getSerializer()); } } return beanProperties; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/translation/handler/TranslationHandler.java ================================================ package top.flya.common.translation.handler; import cn.hutool.core.util.ObjectUtil; 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 top.flya.common.annotation.Translation; import top.flya.common.translation.TranslationInterface; import top.flya.common.utils.StringUtils; import top.flya.common.utils.reflect.ReflectUtils; import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; /** * 翻译处理器 * * @author Lion Li */ @Slf4j public class TranslationHandler extends JsonSerializer implements ContextualSerializer { /** * 全局翻译实现类映射器 */ public static final Map> TRANSLATION_MAPPER = new ConcurrentHashMap<>(); private Translation translation; @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { TranslationInterface trans = TRANSLATION_MAPPER.get(translation.type()); if (ObjectUtil.isNotNull(trans)) { // 如果映射字段不为空 则取映射字段的值 if (StringUtils.isNotBlank(translation.mapper())) { value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper()); } // 如果为 null 直接写出 if (ObjectUtil.isNull(value)) { gen.writeNull(); return; } Object result = trans.translation(value, translation.other()); gen.writeObject(result); } else { gen.writeObject(value); } } @Override public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { Translation translation = property.getAnnotation(Translation.class); if (Objects.nonNull(translation)) { this.translation = translation; return this; } return prov.findValueSerializer(property.getType(), property); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/translation/impl/DeptNameTranslationImpl.java ================================================ package top.flya.common.translation.impl; import top.flya.common.annotation.TranslationType; import top.flya.common.constant.TransConstant; import top.flya.common.core.service.DeptService; import top.flya.common.translation.TranslationInterface; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; /** * 部门翻译实现 * * @author Lion Li */ @Component @AllArgsConstructor @TranslationType(type = TransConstant.DEPT_ID_TO_NAME) public class DeptNameTranslationImpl implements TranslationInterface { private final DeptService deptService; @Override public String translation(Object key, String other) { return deptService.selectDeptNameByIds(key.toString()); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/translation/impl/DictTypeTranslationImpl.java ================================================ package top.flya.common.translation.impl; import top.flya.common.annotation.TranslationType; import top.flya.common.constant.TransConstant; import top.flya.common.core.service.DictService; import top.flya.common.translation.TranslationInterface; import top.flya.common.utils.StringUtils; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; /** * 字典翻译实现 * * @author Lion Li */ @Component @AllArgsConstructor @TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL) public class DictTypeTranslationImpl implements TranslationInterface { private final DictService dictService; @Override public String translation(Object key, String other) { if (key instanceof String && StringUtils.isNotBlank(other)) { return dictService.getDictLabel(other, key.toString()); } return null; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/translation/impl/OssUrlTranslationImpl.java ================================================ package top.flya.common.translation.impl; import top.flya.common.annotation.TranslationType; import top.flya.common.constant.TransConstant; import top.flya.common.core.service.OssService; import top.flya.common.translation.TranslationInterface; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; /** * OSS翻译实现 * * @author Lion Li */ @Component @AllArgsConstructor @TranslationType(type = TransConstant.OSS_ID_TO_URL) public class OssUrlTranslationImpl implements TranslationInterface { private final OssService ossService; @Override public String translation(Object key, String other) { return ossService.selectUrlByIds(key.toString()); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/translation/impl/UserNameTranslationImpl.java ================================================ package top.flya.common.translation.impl; import top.flya.common.annotation.TranslationType; import top.flya.common.constant.TransConstant; import top.flya.common.core.service.UserService; import top.flya.common.translation.TranslationInterface; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; /** * 用户名翻译实现 * * @author Lion Li */ @Component @AllArgsConstructor @TranslationType(type = TransConstant.USER_ID_TO_NAME) public class UserNameTranslationImpl implements TranslationInterface { private final UserService userService; @Override public String translation(Object key, String other) { if (key instanceof Long) { return userService.selectUserNameById((Long) key); } return null; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/BeanCopyUtils.java ================================================ package top.flya.common.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.SimpleCache; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.springframework.cglib.beans.BeanCopier; import org.springframework.cglib.beans.BeanMap; import org.springframework.cglib.core.Converter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * bean深拷贝工具(基于 cglib 性能优异) *

* 重点 cglib 不支持 拷贝到链式对象 * 例如: 源对象 拷贝到 目标(链式对象) * 请区分好`浅拷贝`和`深拷贝`再做使用 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class BeanCopyUtils { /** * 单对象基于class创建拷贝 * * @param source 数据来源实体 * @param desc 描述对象 转换后的对象 * @return desc */ public static V copy(T source, Class desc) { if (ObjectUtil.isNull(source)) { return null; } if (ObjectUtil.isNull(desc)) { return null; } final V target = ReflectUtil.newInstanceIfPossible(desc); return copy(source, target); } /** * 单对象基于对象创建拷贝 * * @param source 数据来源实体 * @param desc 转换后的对象 * @return desc */ public static V copy(T source, V desc) { if (ObjectUtil.isNull(source)) { return null; } if (ObjectUtil.isNull(desc)) { return null; } BeanCopier beanCopier = BeanCopierCache.INSTANCE.get(source.getClass(), desc.getClass(), null); beanCopier.copy(source, desc, null); return desc; } /** * 列表对象基于class创建拷贝 * * @param sourceList 数据来源实体列表 * @param desc 描述对象 转换后的对象 * @return desc */ public static List copyList(List sourceList, Class desc) { if (ObjectUtil.isNull(sourceList)) { return null; } if (CollUtil.isEmpty(sourceList)) { return CollUtil.newArrayList(); } return StreamUtils.toList(sourceList, source -> { V target = ReflectUtil.newInstanceIfPossible(desc); copy(source, target); return target; }); } /** * bean拷贝到map * * @param bean 数据来源实体 * @return map对象 */ @SuppressWarnings("unchecked") public static Map copyToMap(T bean) { if (ObjectUtil.isNull(bean)) { return null; } return BeanMap.create(bean); } /** * map拷贝到bean * * @param map 数据来源 * @param beanClass bean类 * @return bean对象 */ public static T mapToBean(Map map, Class beanClass) { if (MapUtil.isEmpty(map)) { return null; } if (ObjectUtil.isNull(beanClass)) { return null; } T bean = ReflectUtil.newInstanceIfPossible(beanClass); return mapToBean(map, bean); } /** * map拷贝到bean * * @param map 数据来源 * @param bean bean对象 * @return bean对象 */ public static T mapToBean(Map map, T bean) { if (MapUtil.isEmpty(map)) { return null; } if (ObjectUtil.isNull(bean)) { return null; } BeanMap.create(bean).putAll(map); return bean; } /** * map拷贝到map * * @param map 数据来源 * @param clazz 返回的对象类型 * @return map对象 */ public static Map mapToMap(Map map, Class clazz) { if (MapUtil.isEmpty(map)) { return null; } if (ObjectUtil.isNull(clazz)) { return null; } Map copyMap = new LinkedHashMap<>(map.size()); map.forEach((k, v) -> copyMap.put(k, copy(v, clazz))); return copyMap; } /** * BeanCopier属性缓存
* 缓存用于防止多次反射造成的性能问题 * * @author Looly * @since 5.4.1 */ public enum BeanCopierCache { /** * BeanCopier属性缓存单例 */ INSTANCE; private final SimpleCache cache = new SimpleCache<>(); /** * 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素 * * @param srcClass 源Bean的类 * @param targetClass 目标Bean的类 * @param converter 转换器 * @return Map中对应的BeanCopier */ public BeanCopier get(Class srcClass, Class targetClass, Converter converter) { final String key = genKey(srcClass, targetClass, converter); return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, converter != null)); } /** * 获得类与转换器生成的key * * @param srcClass 源Bean的类 * @param targetClass 目标Bean的类 * @param converter 转换器 * @return 属性名和Map映射的key */ private String genKey(Class srcClass, Class targetClass, Converter converter) { final StringBuilder key = StrUtil.builder() .append(srcClass.getName()).append('#').append(targetClass.getName()); if (null != converter) { key.append('#').append(converter.getClass().getName()); } return key.toString(); } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/DateUtils.java ================================================ package top.flya.common.utils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.commons.lang3.time.DateFormatUtils; import java.lang.management.ManagementFactory; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.*; import java.util.Calendar; import java.util.Date; /** * 时间工具类 * * @author ruoyi */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class DateUtils extends org.apache.commons.lang3.time.DateUtils { public static final String YYYY = "yyyy"; public static final String YYYY_MM = "yyyy-MM"; public static final String YYYY_MM_DD = "yyyy-MM-dd"; public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; private static final String[] PARSE_PATTERNS = { "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 String getTime() { return dateTimeNow(YYYY_MM_DD_HH_MM_SS); } public static String dateTimeNow() { return dateTimeNow(YYYYMMDDHHMMSS); } public static String dateTimeNow(final String format) { return parseDateToStr(format, new Date()); } public static String dateTime(final Date date) { return parseDateToStr(YYYY_MM_DD, date); } public static String parseDateToStr(final String format, final Date date) { return new SimpleDateFormat(format).format(date); } public static 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 String datePath() { Date now = new Date(); return DateFormatUtils.format(now, "yyyy/MM/dd"); } /** * 日期路径 即年/月/日 如20180808 */ public static 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(), PARSE_PATTERNS); } 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))); } /** * 计算两个时间差 */ public static String getDatePoor(Date endDate, Date nowDate) { long nd = 1000 * 24 * 60 * 60; long nh = 1000 * 60 * 60; long nm = 1000 * 60; // long ns = 1000; // 获得两个时间的毫秒时间差异 long diff = endDate.getTime() - nowDate.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()); } public static String[] getMonthStartAndEnd(String timeStr) throws ParseException { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = format.parse(timeStr); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); // 获取本月开始时间 calendar.set(Calendar.DAY_OF_MONTH, 1); String monthStart = format.format(calendar.getTime()); // 获取本月结束时间 calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 59); calendar.set(Calendar.SECOND, 59); String monthEnd = format.format(calendar.getTime()); return new String[] { monthStart, monthEnd }; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/EncryptUtils.java ================================================ package top.flya.common.utils; import cn.hutool.core.codec.Base64; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import cn.hutool.crypto.asymmetric.SM2; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; /** * 安全相关工具类 * * @author 老马 */ public class EncryptUtils { /** * 公钥 */ public static final String PUBLIC_KEY = "publicKey"; /** * 私钥 */ public static final String PRIVATE_KEY = "privateKey"; /** * Base64加密 * * @param data 待加密数据 * @return 加密后字符串 */ public static String encryptByBase64(String data) { return Base64.encode(data, StandardCharsets.UTF_8); } /** * Base64解密 * * @param data 待解密数据 * @return 解密后字符串 */ public static String decryptByBase64(String data) { return Base64.decodeStr(data, StandardCharsets.UTF_8); } /** * AES加密 * * @param data 待解密数据 * @param password 秘钥字符串 * @return 加密后字符串, 采用Base64编码 */ public static String encryptByAes(String data, String password) { if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("AES需要传入秘钥信息"); } // aes算法的秘钥要求是16位、24位、32位 int[] array = {16, 24, 32}; if (!ArrayUtil.contains(array, password.length())) { throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); } return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); } /** * AES解密 * * @param data 待解密数据 * @param password 秘钥字符串 * @return 解密后字符串 */ public static String decryptByAes(String data, String password) { if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("AES需要传入秘钥信息"); } // aes算法的秘钥要求是16位、24位、32位 int[] array = {16, 24, 32}; if (!ArrayUtil.contains(array, password.length())) { throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); } return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); } /** * sm4加密 * * @param data 待加密数据 * @param password 秘钥字符串 * @return 加密后字符串, 采用Base64编码 */ public static String encryptBySm4(String data, String password) { if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("SM4需要传入秘钥信息"); } // sm4算法的秘钥要求是16位长度 int sm4PasswordLength = 16; if (sm4PasswordLength != password.length()) { throw new IllegalArgumentException("SM4秘钥长度要求为16位"); } return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); } /** * sm4解密 * * @param data 待解密数据 * @param password 秘钥字符串 * @return 解密后字符串 */ public static String decryptBySm4(String data, String password) { if (StrUtil.isBlank(password)) { throw new IllegalArgumentException("SM4需要传入秘钥信息"); } // sm4算法的秘钥要求是16位长度 int sm4PasswordLength = 16; if (sm4PasswordLength != password.length()) { throw new IllegalArgumentException("SM4秘钥长度要求为16位"); } return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); } /** * 产生sm2加解密需要的公钥和私钥 * * @return 公私钥Map */ public static Map generateSm2Key() { Map keyMap = new HashMap<>(2); SM2 sm2 = SmUtil.sm2(); keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64()); keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64()); return keyMap; } /** * sm2公钥加密 * * @param data 待加密数据 * @param publicKey 公钥 * @return 加密后字符串, 采用Base64编码 */ public static String encryptBySm2(String data, String publicKey) { if (StrUtil.isBlank(publicKey)) { throw new IllegalArgumentException("SM2需要传入公钥进行加密"); } SM2 sm2 = SmUtil.sm2(null, publicKey); return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); } /** * sm2私钥解密 * * @param data 待加密数据 * @param privateKey 私钥 * @return 解密后字符串 */ public static String decryptBySm2(String data, String privateKey) { if (StrUtil.isBlank(privateKey)) { throw new IllegalArgumentException("SM2需要传入私钥进行解密"); } SM2 sm2 = SmUtil.sm2(privateKey, null); return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); } /** * 产生RSA加解密需要的公钥和私钥 * * @return 公私钥Map */ public static Map generateRsaKey() { Map keyMap = new HashMap<>(2); RSA rsa = SecureUtil.rsa(); keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64()); keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64()); return keyMap; } /** * rsa公钥加密 * * @param data 待加密数据 * @param publicKey 公钥 * @return 加密后字符串, 采用Base64编码 */ public static String encryptByRsa(String data, String publicKey) { if (StrUtil.isBlank(publicKey)) { throw new IllegalArgumentException("RSA需要传入公钥进行加密"); } RSA rsa = SecureUtil.rsa(null, publicKey); return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); } /** * rsa私钥解密 * * @param data 待加密数据 * @param privateKey 私钥 * @return 解密后字符串 */ public static String decryptByRsa(String data, String privateKey) { if (StrUtil.isBlank(privateKey)) { throw new IllegalArgumentException("RSA需要传入私钥进行解密"); } RSA rsa = SecureUtil.rsa(privateKey, null); return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); } /** * md5加密 * * @param data 待加密数据 * @return 加密后字符串, 采用Hex编码 */ public static String encryptByMd5(String data) { return SecureUtil.md5(data); } /** * sha256加密 * * @param data 待加密数据 * @return 加密后字符串, 采用Hex编码 */ public static String encryptBySha256(String data) { return SecureUtil.sha256(data); } /** * sm3加密 * * @param data 待加密数据 * @return 加密后字符串, 采用Hex编码 */ public static String encryptBySm3(String data) { return SmUtil.sm3(data); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/JsonUtils.java ================================================ package top.flya.common.utils; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * JSON 工具类 * * @author 芋道源码 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class JsonUtils { private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class); public static ObjectMapper getObjectMapper() { return OBJECT_MAPPER; } public static String toJsonString(Object object) { if (ObjectUtil.isNull(object)) { return null; } try { return OBJECT_MAPPER.writeValueAsString(object); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } public static T parseObject(String text, Class clazz) { if (StringUtils.isEmpty(text)) { return null; } try { return OBJECT_MAPPER.readValue(text, clazz); } catch (IOException e) { throw new RuntimeException(e); } } public static T parseObject(byte[] bytes, Class clazz) { if (ArrayUtil.isEmpty(bytes)) { return null; } try { return OBJECT_MAPPER.readValue(bytes, clazz); } catch (IOException e) { throw new RuntimeException(e); } } public static T parseObject(String text, TypeReference typeReference) { if (StringUtils.isBlank(text)) { return null; } try { return OBJECT_MAPPER.readValue(text, typeReference); } catch (IOException e) { throw new RuntimeException(e); } } public static Dict parseMap(String text) { if (StringUtils.isBlank(text)) { return null; } try { return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class)); } catch (MismatchedInputException e) { // 类型不匹配说明不是json return null; } catch (IOException e) { throw new RuntimeException(e); } } public static List parseArrayMap(String text) { if (StringUtils.isBlank(text)) { return null; } try { return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class)); } catch (IOException e) { throw new RuntimeException(e); } } public static List parseArray(String text, Class clazz) { if (StringUtils.isEmpty(text)) { return new ArrayList<>(); } try { return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); } catch (IOException e) { throw new RuntimeException(e); } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/MessageUtils.java ================================================ package top.flya.common.utils; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; /** * 获取i18n资源文件 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class MessageUtils { private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class); /** * 根据消息键和参数 获取消息 委托给spring messageSource * * @param code 消息键 * @param args 参数 * @return 获取国际化翻译值 */ public static String message(String code, Object... args) { return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale()); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/ServletUtils.java ================================================ package top.flya.common.utils; import cn.hutool.core.convert.Convert; import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.http.HttpStatus; import top.flya.common.constant.Constants; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.springframework.http.MediaType; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * 客户端工具类 * * @author ruoyi */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ServletUtils extends ServletUtil { /** * 获取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 getParams(ServletRequest request) { final Map map = request.getParameterMap(); return Collections.unmodifiableMap(map); } /** * 获得所有请求参数 * * @param request 请求对象{@link ServletRequest} * @return Map */ public static Map getParamMap(ServletRequest request) { Map params = new HashMap<>(); for (Map.Entry entry : getParams(request).entrySet()) { params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR)); } 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(HttpStatus.HTTP_OK); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); 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(MediaType.APPLICATION_JSON_VALUE)) { return true; } String xRequestedWith = request.getHeader("X-Requested-With"); if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { return true; } String uri = request.getRequestURI(); if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { return true; } String ajax = request.getParameter("__ajax"); return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml"); } public static String getClientIP() { return getClientIP(getRequest()); } /** * 内容编码 * * @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; } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/StreamUtils.java ================================================ package top.flya.common.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; /** * stream 流工具类 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class StreamUtils { /** * 将collection过滤 * * @param collection 需要转化的集合 * @param function 过滤方法 * @return 过滤后的list */ public static List filter(Collection collection, Predicate function) { if (CollUtil.isEmpty(collection)) { return CollUtil.newArrayList(); } return collection.stream().filter(function).collect(Collectors.toList()); } /** * 将collection拼接 * * @param collection 需要转化的集合 * @param function 拼接方法 * @return 拼接后的list */ public static String join(Collection collection, Function function) { return join(collection, function, StringUtils.SEPARATOR); } /** * 将collection拼接 * * @param collection 需要转化的集合 * @param function 拼接方法 * @param delimiter 拼接符 * @return 拼接后的list */ public static String join(Collection collection, Function function, CharSequence delimiter) { if (CollUtil.isEmpty(collection)) { return StringUtils.EMPTY; } return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter)); } /** * 将collection排序 * * @param collection 需要转化的集合 * @param comparing 排序方法 * @return 排序后的list */ public static List sorted(Collection collection, Comparator comparing) { if (CollUtil.isEmpty(collection)) { return CollUtil.newArrayList(); } return collection.stream().sorted(comparing).collect(Collectors.toList()); } /** * 将collection转化为类型不变的map
* {@code Collection ----> Map} * * @param collection 需要转化的集合 * @param key V类型转化为K类型的lambda方法 * @param collection中的泛型 * @param map中的key类型 * @return 转化后的map */ public static Map toIdentityMap(Collection collection, Function key) { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); } /** * 将Collection转化为map(value类型与collection的泛型不同)
* {@code Collection -----> Map } * * @param collection 需要转化的集合 * @param key E类型转化为K类型的lambda方法 * @param value E类型转化为V类型的lambda方法 * @param collection中的泛型 * @param map中的key类型 * @param map中的value类型 * @return 转化后的map */ public static Map toMap(Collection collection, Function key, Function value) { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l)); } /** * 将collection按照规则(比如有相同的班级id)分类成map
* {@code Collection -------> Map> } * * @param collection 需要分类的集合 * @param key 分类的规则 * @param collection中的泛型 * @param map中的key类型 * @return 分类后的map */ public static Map> groupByKey(Collection collection, Function key) { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } return collection .stream() .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList())); } /** * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
* {@code Collection ---> Map>> } * * @param collection 需要分类的集合 * @param key1 第一个分类的规则 * @param key2 第二个分类的规则 * @param 集合元素类型 * @param 第一个map中的key类型 * @param 第二个map中的key类型 * @return 分类后的map */ public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) { if (CollUtil.isEmpty(collection)) { return MapUtil.newHashMap(); } return collection .stream() .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList()))); } /** * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
* {@code Collection ---> Map> } * * @param collection 需要分类的集合 * @param key1 第一个分类的规则 * @param key2 第二个分类的规则 * @param 第一个map中的key类型 * @param 第二个map中的key类型 * @param collection中的泛型 * @return 分类后的map */ public static Map> group2Map(Collection collection, Function key1, Function key2) { if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { return MapUtil.newHashMap(); } return collection .stream() .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); } /** * 将collection转化为List集合,但是两者的泛型不同
* {@code Collection ------> List } * * @param collection 需要转化的集合 * @param function collection中的泛型转化为list泛型的lambda表达式 * @param collection中的泛型 * @param List中的泛型 * @return 转化后的list */ public static List toList(Collection collection, Function function) { if (CollUtil.isEmpty(collection)) { return CollUtil.newArrayList(); } return collection .stream() .map(function) .filter(Objects::nonNull) .collect(Collectors.toList()); } /** * 将collection转化为Set集合,但是两者的泛型不同
* {@code Collection ------> Set } * * @param collection 需要转化的集合 * @param function collection中的泛型转化为set泛型的lambda表达式 * @param collection中的泛型 * @param Set中的泛型 * @return 转化后的Set */ public static Set toSet(Collection collection, Function function) { if (CollUtil.isEmpty(collection) || function == null) { return CollUtil.newHashSet(); } return collection .stream() .map(function) .filter(Objects::nonNull) .collect(Collectors.toSet()); } /** * 合并两个相同key类型的map * * @param map1 第一个需要合并的 map * @param map2 第二个需要合并的 map * @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况 * @param map中的key类型 * @param 第一个 map的value类型 * @param 第二个 map的value类型 * @param 最终map的value类型 * @return 合并后的map */ public static Map merge(Map map1, Map map2, BiFunction merge) { if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) { return MapUtil.newHashMap(); } else if (MapUtil.isEmpty(map1)) { map1 = MapUtil.newHashMap(); } else if (MapUtil.isEmpty(map2)) { map2 = MapUtil.newHashMap(); } Set key = new HashSet<>(); key.addAll(map1.keySet()); key.addAll(map2.keySet()); Map map = new HashMap<>(); for (K t : key) { X x = map1.get(t); Y y = map2.get(t); V z = merge.apply(x, y); if (z != null) { map.put(t, z); } } return map; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/StringUtils.java ================================================ package top.flya.common.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.springframework.util.AntPathMatcher; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; /** * 字符串工具类 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class StringUtils extends org.apache.commons.lang3.StringUtils { public static final String SEPARATOR = ","; /** * 获取参数不为空值 * * @param str defaultValue 要判断的value * @return value 返回值 */ public static String blankToDefault(String str, String defaultValue) { return StrUtil.blankToDefault(str, defaultValue); } /** * * 判断一个字符串是否为空串 * * @param str String * @return true:为空 false:非空 */ public static boolean isEmpty(String str) { return StrUtil.isEmpty(str); } /** * * 判断一个字符串是否为非空串 * * @param str String * @return true:非空串 false:空串 */ public static boolean isNotEmpty(String str) { return !isEmpty(str); } /** * 去空格 */ public static String trim(String str) { return StrUtil.trim(str); } /** * 截取字符串 * * @param str 字符串 * @param start 开始 * @return 结果 */ public static String substring(final String str, int start) { return substring(str, start, str.length()); } /** * 截取字符串 * * @param str 字符串 * @param start 开始 * @param end 结束 * @return 结果 */ public static String substring(final String str, int start, int end) { return StrUtil.sub(str, start, end); } /** * 格式化文本, {} 表示占位符
* 此方法只是简单将占位符 {} 按照顺序替换为参数
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
* 例:
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is {} for a
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
* * @param template 文本模板,被替换的部分用 {} 表示 * @param params 参数值 * @return 格式化后的文本 */ public static String format(String template, Object... params) { return StrUtil.format(template, params); } /** * 是否为http(s)://开头 * * @param link 链接 * @return 结果 */ public static boolean ishttp(String link) { return Validator.isUrl(link); } /** * 字符串转set * * @param str 字符串 * @param sep 分隔符 * @return set集合 */ public static Set str2Set(String str, String sep) { return new HashSet<>(str2List(str, sep, true, false)); } /** * 字符串转list * * @param str 字符串 * @param sep 分隔符 * @param filterBlank 过滤纯空白 * @param trim 去掉首尾空白 * @return list集合 */ public static List str2List(String str, String sep, boolean filterBlank, boolean trim) { List list = new ArrayList<>(); if (isEmpty(str)) { return list; } // 过滤空白字符串 if (filterBlank && isBlank(str)) { return list; } String[] split = str.split(sep); for (String string : split) { if (filterBlank && isBlank(string)) { continue; } if (trim) { string = trim(string); } list.add(string); } return list; } /** * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 * * @param cs 指定字符串 * @param searchCharSequences 需要检查的字符串数组 * @return 是否包含任意一个字符串 */ public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) { return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences); } /** * 驼峰转下划线命名 */ public static String toUnderScoreCase(String str) { return StrUtil.toUnderlineCase(str); } /** * 是否包含字符串 * * @param str 验证字符串 * @param strs 字符串组 * @return 包含返回true */ public static boolean inStringIgnoreCase(String str, String... strs) { return StrUtil.equalsAnyIgnoreCase(str, strs); } /** * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld * * @param name 转换前的下划线大写方式命名的字符串 * @return 转换后的驼峰式命名的字符串 */ public static String convertToCamelCase(String name) { return StrUtil.upperFirst(StrUtil.toCamelCase(name)); } /** * 驼峰式命名法 例如:user_name->userName */ public static String toCamelCase(String s) { return StrUtil.toCamelCase(s); } /** * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 * * @param str 指定字符串 * @param strs 需要检查的字符串数组 * @return 是否匹配 */ public static boolean matches(String str, List strs) { if (isEmpty(str) || CollUtil.isEmpty(strs)) { return false; } for (String pattern : strs) { if (isMatch(pattern, str)) { return true; } } return false; } /** * 判断url是否与规则配置: * ? 表示单个字符; * * 表示一层路径内的任意字符串,不可跨层级; * ** 表示任意层路径; * * @param pattern 匹配规则 * @param url 需要匹配的url */ public static boolean isMatch(String pattern, String url) { AntPathMatcher matcher = new AntPathMatcher(); return matcher.match(pattern, url); } /** * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 * * @param num 数字对象 * @param size 字符串指定长度 * @return 返回数字的字符串格式,该字符串为指定长度。 */ public static 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 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(); } /** * 切分字符串(分隔符默认逗号) * * @param str 被切分的字符串 * @return 分割后的数据列表 */ public static List splitList(String str) { return splitTo(str, Convert::toStr); } /** * 切分字符串 * * @param str 被切分的字符串 * @param separator 分隔符 * @return 分割后的数据列表 */ public static List splitList(String str, String separator) { return splitTo(str, separator, Convert::toStr); } /** * 切分字符串自定义转换(分隔符默认逗号) * * @param str 被切分的字符串 * @param mapper 自定义转换 * @return 分割后的数据列表 */ public static List splitTo(String str, Function mapper) { return splitTo(str, SEPARATOR, mapper); } /** * 切分字符串自定义转换 * * @param str 被切分的字符串 * @param separator 分隔符 * @param mapper 自定义转换 * @return 分割后的数据列表 */ public static List splitTo(String str, String separator, Function mapper) { if (isBlank(str)) { return new ArrayList<>(0); } return StrUtil.split(str, separator) .stream() .filter(Objects::nonNull) .map(mapper) .collect(Collectors.toList()); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/Threads.java ================================================ package top.flya.common.utils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.*; /** * 线程相关工具类. * * @author ruoyi */ @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class Threads { /** * 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)) { log.info("Pool did not terminate"); } } } catch (InterruptedException ie) { pool.shutdownNow(); Thread.currentThread().interrupt(); } } } /** * 打印线程异常信息 */ public static void printException(Runnable r, Throwable t) { if (t == null && r instanceof Future) { try { Future future = (Future) r; if (future.isDone()) { future.get(); } } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } if (t != null) { log.error(t.getMessage(), t); } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/TreeBuildUtils.java ================================================ package top.flya.common.utils; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeNodeConfig; import cn.hutool.core.lang.tree.TreeUtil; import cn.hutool.core.lang.tree.parser.NodeParser; import top.flya.common.utils.reflect.ReflectUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.util.List; /** * 扩展 hutool TreeUtil 封装系统树构建 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class TreeBuildUtils extends TreeUtil { /** * 根据前端定制差异化字段 */ public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label"); public static List> build(List list, NodeParser nodeParser) { if (CollUtil.isEmpty(list)) { return null; } K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/ValidatorUtils.java ================================================ package top.flya.common.utils; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.Validator; import java.util.Set; /** * Validator 校验框架工具 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ValidatorUtils { private static final Validator VALID = SpringUtils.getBean(Validator.class); public static void validate(T object, Class... groups) { Set> validate = VALID.validate(object, groups); if (!validate.isEmpty()) { throw new ConstraintViolationException("参数校验异常", validate); } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/email/MailUtils.java ================================================ package top.flya.common.utils.email; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.mail.*; import top.flya.common.utils.StringUtils; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import javax.mail.Authenticator; import javax.mail.Session; import java.io.File; import java.io.InputStream; import java.util.Collection; import java.util.List; import java.util.Map; /** * 邮件工具类 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class MailUtils { private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); /** * 获取邮件发送实例 */ public static MailAccount getMailAccount() { return ACCOUNT; } /** * 获取邮件发送实例 (自定义发送人以及授权码) * * @param user 发送人 * @param pass 授权码 */ public static MailAccount getMailAccount(String from, String user, String pass) { ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser())); ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass())); return ACCOUNT; } /** * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 * * @param to 收件人 * @param subject 标题 * @param content 正文 * @param files 附件列表 * @return message-id * @since 3.2.0 */ public static String sendText(String to, String subject, String content, File... files) { return send(to, subject, content, false, files); } /** * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 * * @param to 收件人 * @param subject 标题 * @param content 正文 * @param files 附件列表 * @return message-id * @since 3.2.0 */ public static String sendHtml(String to, String subject, String content, File... files) { return send(to, subject, content, true, files); } /** * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 * * @param to 收件人 * @param subject 标题 * @param content 正文 * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id */ public static String send(String to, String subject, String content, boolean isHtml, File... files) { return send(splitAddress(to), subject, content, isHtml, files); } /** * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 * * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 * @param subject 标题 * @param content 正文 * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id * @since 4.0.3 */ public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) { return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files); } /** * 使用配置文件中设置的账户发送文本邮件,发送给多人 * * @param tos 收件人列表 * @param subject 标题 * @param content 正文 * @param files 附件列表 * @return message-id */ public static String sendText(Collection tos, String subject, String content, File... files) { return send(tos, subject, content, false, files); } /** * 使用配置文件中设置的账户发送HTML邮件,发送给多人 * * @param tos 收件人列表 * @param subject 标题 * @param content 正文 * @param files 附件列表 * @return message-id * @since 3.2.0 */ public static String sendHtml(Collection tos, String subject, String content, File... files) { return send(tos, subject, content, true, files); } /** * 使用配置文件中设置的账户发送邮件,发送给多人 * * @param tos 收件人列表 * @param subject 标题 * @param content 正文 * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id */ public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) { return send(tos, null, null, subject, content, isHtml, files); } /** * 使用配置文件中设置的账户发送邮件,发送给多人 * * @param tos 收件人列表 * @param ccs 抄送人列表,可以为null或空 * @param bccs 密送人列表,可以为null或空 * @param subject 标题 * @param content 正文 * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id * @since 4.0.3 */ public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files); } // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount /** * 发送邮件给多人 * * @param mailAccount 邮件认证对象 * @param to 收件人,多个收件人逗号或者分号隔开 * @param subject 标题 * @param content 正文 * @param isHtml 是否为HTML格式 * @param files 附件列表 * @return message-id * @since 3.2.0 */ public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) { return send(mailAccount, splitAddress(to), subject, content, isHtml, files); } /** * 发送邮件给多人 * * @param mailAccount 邮件帐户信息 * @param tos 收件人列表 * @param subject 标题 * @param content 正文 * @param isHtml 是否为HTML格式 * @param files 附件列表 * @return message-id */ public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) { return send(mailAccount, tos, null, null, subject, content, isHtml, files); } /** * 发送邮件给多人 * * @param mailAccount 邮件帐户信息 * @param tos 收件人列表 * @param ccs 抄送人列表,可以为null或空 * @param bccs 密送人列表,可以为null或空 * @param subject 标题 * @param content 正文 * @param isHtml 是否为HTML格式 * @param files 附件列表 * @return message-id * @since 4.0.3 */ public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files); } /** * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 * * @param to 收件人 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param files 附件列表 * @return message-id * @since 3.2.0 */ public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) { return send(to, subject, content, imageMap, true, files); } /** * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 * * @param to 收件人 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id */ public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { return send(splitAddress(to), subject, content, imageMap, isHtml, files); } /** * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
* 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 * * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id * @since 4.0.3 */ public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) { return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files); } /** * 使用配置文件中设置的账户发送HTML邮件,发送给多人 * * @param tos 收件人列表 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param files 附件列表 * @return message-id * @since 3.2.0 */ public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) { return send(tos, subject, content, imageMap, true, files); } /** * 使用配置文件中设置的账户发送邮件,发送给多人 * * @param tos 收件人列表 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id */ public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { return send(tos, null, null, subject, content, imageMap, isHtml, files); } /** * 使用配置文件中设置的账户发送邮件,发送给多人 * * @param tos 收件人列表 * @param ccs 抄送人列表,可以为null或空 * @param bccs 密送人列表,可以为null或空 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param isHtml 是否为HTML * @param files 附件列表 * @return message-id * @since 4.0.3 */ public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files); } // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount /** * 发送邮件给多人 * * @param mailAccount 邮件认证对象 * @param to 收件人,多个收件人逗号或者分号隔开 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param isHtml 是否为HTML格式 * @param files 附件列表 * @return message-id * @since 3.2.0 */ public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files); } /** * 发送邮件给多人 * * @param mailAccount 邮件帐户信息 * @param tos 收件人列表 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param isHtml 是否为HTML格式 * @param files 附件列表 * @return message-id * @since 4.6.3 */ public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files); } /** * 发送邮件给多人 * * @param mailAccount 邮件帐户信息 * @param tos 收件人列表 * @param ccs 抄送人列表,可以为null或空 * @param bccs 密送人列表,可以为null或空 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER * @param isHtml 是否为HTML格式 * @param files 附件列表 * @return message-id * @since 4.6.3 */ public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files); } /** * 根据配置文件,获取邮件客户端会话 * * @param mailAccount 邮件账户配置 * @param isSingleton 是否单例(全局共享会话) * @return {@link Session} * @since 5.5.7 */ public static Session getSession(MailAccount mailAccount, boolean isSingleton) { Authenticator authenticator = null; if (mailAccount.isAuth()) { authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); } return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // : Session.getInstance(mailAccount.getSmtpProps(), authenticator); } // ------------------------------------------------------------------------------------------------------------------------ Private method start /** * 发送邮件给多人 * * @param mailAccount 邮件帐户信息 * @param useGlobalSession 是否全局共享Session * @param tos 收件人列表 * @param ccs 抄送人列表,可以为null或空 * @param bccs 密送人列表,可以为null或空 * @param subject 标题 * @param content 正文 * @param imageMap 图片与占位符,占位符格式为cid:${cid} * @param isHtml 是否为HTML格式 * @param files 附件列表 * @return message-id * @since 4.6.3 */ private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession); // 可选抄送人 if (CollUtil.isNotEmpty(ccs)) { mail.setCcs(ccs.toArray(new String[0])); } // 可选密送人 if (CollUtil.isNotEmpty(bccs)) { mail.setBccs(bccs.toArray(new String[0])); } mail.setTos(tos.toArray(new String[0])); mail.setTitle(subject); mail.setContent(content); mail.setHtml(isHtml); mail.setFiles(files); // 图片 if (MapUtil.isNotEmpty(imageMap)) { for (Map.Entry entry : imageMap.entrySet()) { mail.addImage(entry.getKey(), entry.getValue()); // 关闭流 IoUtil.close(entry.getValue()); } } return mail.send(); } /** * 将多个联系人转为列表,分隔符为逗号或者分号 * * @param addresses 多个联系人,如果为空返回null * @return 联系人列表 */ private static List splitAddress(String addresses) { if (StrUtil.isBlank(addresses)) { return null; } List result; if (StrUtil.contains(addresses, CharUtil.COMMA)) { result = StrUtil.splitTrim(addresses, CharUtil.COMMA); } else if (StrUtil.contains(addresses, ';')) { result = StrUtil.splitTrim(addresses, ';'); } else { result = CollUtil.newArrayList(addresses); } return result; } // ------------------------------------------------------------------------------------------------------------------------ Private method end } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/file/FileUtils.java ================================================ package top.flya.common.utils.file; import cn.hutool.core.io.FileUtil; import lombok.AccessLevel; import lombok.NoArgsConstructor; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; /** * 文件处理工具类 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class FileUtils extends FileUtil { /** * 下载文件名重新编码 * * @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); } /** * 百分号编码工具方法 * * @param s 需要百分号编码的字符串 * @return 百分号编码后的字符串 */ public static String percentEncode(String s) throws UnsupportedEncodingException { String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); return encode.replaceAll("\\+", "%20"); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/file/MimeTypeUtils.java ================================================ package top.flya.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"}; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/ip/AddressUtils.java ================================================ package top.flya.common.utils.ip; import cn.hutool.core.net.NetUtil; import cn.hutool.http.HtmlUtil; import top.flya.common.utils.StringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; /** * 获取地址类 * * @author Lion Li */ @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class AddressUtils { // 未知地址 public static final String UNKNOWN = "XX XX"; public static String getRealAddressByIP(String ip) { if (StringUtils.isBlank(ip)) { return UNKNOWN; } // 内网不查询 ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); if (NetUtil.isInnerIP(ip)) { return "内网IP"; } return RegionUtils.getCityInfo(ip); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/ip/RegionUtils.java ================================================ package top.flya.common.utils.ip; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.resource.ClassPathResource; import cn.hutool.core.util.ObjectUtil; import top.flya.common.exception.ServiceException; import top.flya.common.utils.file.FileUtils; import lombok.extern.slf4j.Slf4j; import org.lionsoul.ip2region.xdb.Searcher; import java.io.File; /** * 根据ip地址定位工具类,离线方式 * 参考地址:集成 ip2region 实现离线IP地址定位库 * * @author lishuyan */ @Slf4j public class RegionUtils { private static final Searcher SEARCHER; static { String fileName = "/ip2region.xdb"; File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName); if (!FileUtils.exist(existFile)) { ClassPathResource fileStream = new ClassPathResource(fileName); if (ObjectUtil.isEmpty(fileStream.getStream())) { throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); } FileUtils.writeFromStream(fileStream.getStream(), existFile); } String dbPath = existFile.getPath(); // 1、从 dbPath 加载整个 xdb 到内存。 byte[] cBuff; try { cBuff = Searcher.loadContentFromFile(dbPath); } catch (Exception e) { throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage()); } // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 try { SEARCHER = Searcher.newWithBuffer(cBuff); } catch (Exception e) { throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage()); } } /** * 根据IP地址离线获取城市 */ public static String getCityInfo(String ip) { try { ip = ip.trim(); // 3、执行查询 String region = SEARCHER.search(ip); return region.replace("0|", "").replace("|0", ""); } catch (Exception e) { log.error("IP地址离线获取城市异常 {}", ip); return "未知"; } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/poi/ExcelUtil.java ================================================ package top.flya.common.utils.poi; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.resource.ClassPathResource; import cn.hutool.core.util.IdUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; import com.alibaba.excel.write.metadata.fill.FillWrapper; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import top.flya.common.convert.ExcelBigNumberConvert; import top.flya.common.excel.CellMergeStrategy; import top.flya.common.excel.DefaultExcelListener; import top.flya.common.excel.ExcelListener; import top.flya.common.excel.ExcelResult; import top.flya.common.utils.StringUtils; import top.flya.common.utils.file.FileUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.List; import java.util.Map; /** * Excel相关处理 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ExcelUtil { /** * 同步导入(适用于小数据量) * * @param is 输入流 * @return 转换后集合 */ public static List importExcel(InputStream is, Class clazz) { return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); } /** * 使用校验监听器 异步导入 同步返回 * * @param is 输入流 * @param clazz 对象类型 * @param isValidate 是否 Validator 检验 默认为是 * @return 转换后集合 */ public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); EasyExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } /** * 使用自定义监听器 异步导入 自定义返回 * * @param is 输入流 * @param clazz 对象类型 * @param listener 自定义监听器 * @return 转换后集合 */ public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { EasyExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param response 响应体 */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, false, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param merge 是否合并单元格 * @param response 响应体 */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, merge, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param os 输出流 */ public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { exportExcel(list, sheetName, clazz, false, os); } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param merge 是否合并单元格 * @param os 输出流 */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os) { ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .sheet(sheetName); if (merge) { // 合并处理器 builder.registerWriteHandler(new CellMergeStrategy(list, true)); } builder.doWrite(list); } /** * 单表多数据模板导出 模板格式为 {.属性} * * @param filename 文件名 * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param response 响应体 */ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplate(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 单表多数据模板导出 模板格式为 {.属性} * * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param os 输出流 */ public static void exportTemplate(List data, String templatePath, OutputStream os) { ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } // 单表多数据导出 模板格式为 {.属性} for (Object d : data) { excelWriter.fill(d, writeSheet); } excelWriter.finish(); } /** * 多表多数据模板导出 模板格式为 {key.属性} * * @param filename 文件名 * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param response 响应体 */ public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { try { resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplateMultiList(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 多表多数据模板导出 模板格式为 {key.属性} * * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param os 输出流 */ public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } for (Map.Entry map : data.entrySet()) { // 设置列表后续还有数据 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); if (map.getValue() instanceof Collection) { // 多表导出必须使用 FillWrapper excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); } else { excelWriter.fill(map.getValue(), writeSheet); } } excelWriter.finish(); } /** * 重置响应体 */ private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { String filename = encodingFilename(sheetName); FileUtils.setAttachmentResponseHeader(response, filename); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); } /** * 解析导出值 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(StringUtils.SEPARATOR); 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(StringUtils.SEPARATOR); 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); } /** * 编码文件名 */ public static String encodingFilename(String filename) { return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx"; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/redis/CacheUtils.java ================================================ package top.flya.common.utils.redis; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.redisson.api.RMap; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import java.util.Set; /** * 缓存操作工具类 {@link } * * @author Michelle.Chung * @date 2022/8/13 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) @SuppressWarnings(value = {"unchecked"}) public class CacheUtils { private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class); /** * 获取缓存组内所有的KEY * * @param cacheNames 缓存组名称 */ public static Set keys(String cacheNames) { RMap rmap = (RMap) CACHE_MANAGER.getCache(cacheNames).getNativeCache(); return rmap.keySet(); } /** * 获取缓存值 * * @param cacheNames 缓存组名称 * @param key 缓存key */ public static T get(String cacheNames, Object key) { Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key); return wrapper != null ? (T) wrapper.get() : null; } /** * 保存缓存值 * * @param cacheNames 缓存组名称 * @param key 缓存key * @param value 缓存值 */ public static void put(String cacheNames, Object key, Object value) { CACHE_MANAGER.getCache(cacheNames).put(key, value); } /** * 删除缓存值 * * @param cacheNames 缓存组名称 * @param key 缓存key */ public static void evict(String cacheNames, Object key) { CACHE_MANAGER.getCache(cacheNames).evict(key); } /** * 清空缓存值 * * @param cacheNames 缓存组名称 */ public static void clear(String cacheNames) { CACHE_MANAGER.getCache(cacheNames).clear(); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/redis/QueueUtils.java ================================================ package top.flya.common.utils.redis; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.redisson.api.*; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; /** * 分布式队列工具 * 轻量级队列 重量级数据量 请使用 MQ * 要求 redis 5.X 以上 * * @author Lion Li * @version 3.6.0 新增 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class QueueUtils { private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); /** * 获取客户端实例 */ public static RedissonClient getClient() { return CLIENT; } /** * 添加普通队列数据 * * @param queueName 队列名 * @param data 数据 */ public static boolean addQueueObject(String queueName, T data) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); return queue.offer(data); } /** * 通用获取一个队列数据 没有数据返回 null(不支持延迟队列) * * @param queueName 队列名 */ public static T getQueueObject(String queueName) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); return queue.poll(); } /** * 通用删除队列数据(不支持延迟队列) */ public static boolean removeQueueObject(String queueName, T data) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); return queue.remove(data); } /** * 通用销毁队列 所有阻塞监听 报错(不支持延迟队列) */ public static boolean destroyQueue(String queueName) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); return queue.delete(); } /** * 添加延迟队列数据 默认毫秒 * * @param queueName 队列名 * @param data 数据 * @param time 延迟时间 */ public static void addDelayedQueueObject(String queueName, T data, long time) { addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS); } /** * 添加延迟队列数据 * * @param queueName 队列名 * @param data 数据 * @param time 延迟时间 * @param timeUnit 单位 */ public static void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); delayedQueue.offer(data, time, timeUnit); } /** * 获取一个延迟队列数据 没有数据返回 null * * @param queueName 队列名 */ public static T getDelayedQueueObject(String queueName) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); return delayedQueue.poll(); } /** * 删除延迟队列数据 */ public static boolean removeDelayedQueueObject(String queueName, T data) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); return delayedQueue.remove(data); } /** * 销毁延迟队列 所有阻塞监听 报错 */ public static void destroyDelayedQueue(String queueName) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); RDelayedQueue delayedQueue = CLIENT.getDelayedQueue(queue); delayedQueue.destroy(); } /** * 添加优先队列数据 * * @param queueName 队列名 * @param data 数据 */ public static boolean addPriorityQueueObject(String queueName, T data) { RPriorityBlockingQueue priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName); return priorityBlockingQueue.offer(data); } /** * 尝试设置 有界队列 容量 用于限制数量 * * @param queueName 队列名 * @param capacity 容量 */ public static boolean trySetBoundedQueueCapacity(String queueName, int capacity) { RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); return boundedBlockingQueue.trySetCapacity(capacity); } /** * 尝试设置 有界队列 容量 用于限制数量 * * @param queueName 队列名 * @param capacity 容量 * @param destroy 已存在是否销毁 */ public static boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) { RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); if (boundedBlockingQueue.isExists() && destroy) { destroyQueue(queueName); } return boundedBlockingQueue.trySetCapacity(capacity); } /** * 添加有界队列数据 * * @param queueName 队列名 * @param data 数据 * @return 添加成功 true 已达到界限 false */ public static boolean addBoundedQueueObject(String queueName, T data) { RBoundedBlockingQueue boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName); return boundedBlockingQueue.offer(data); } /** * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等) */ public static void subscribeBlockingQueue(String queueName, Consumer consumer) { RBlockingQueue queue = CLIENT.getBlockingQueue(queueName); queue.subscribeOnElements(consumer); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/redis/RedisUtils.java ================================================ package top.flya.common.utils.redis; import top.flya.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.redisson.api.*; import java.time.Duration; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; /** * redis 工具类 * * @author Lion Li * @version 3.1.0 新增 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) @SuppressWarnings(value = {"unchecked", "rawtypes"}) public class RedisUtils { private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); /** * 限流 * * @param key 限流key * @param rateType 限流类型 * @param rate 速率 * @param rateInterval 速率间隔 * @return -1 表示失败 */ public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) { RRateLimiter rateLimiter = CLIENT.getRateLimiter(key); rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS); if (rateLimiter.tryAcquire()) { return rateLimiter.availablePermits(); } else { return -1L; } } /** * 获取客户端实例 */ public static RedissonClient getClient() { return CLIENT; } /** * 发布通道消息 * * @param channelKey 通道key * @param msg 发送数据 * @param consumer 自定义处理 */ public static void publish(String channelKey, T msg, Consumer consumer) { RTopic topic = CLIENT.getTopic(channelKey); topic.publish(msg); consumer.accept(msg); } public static void publish(String channelKey, T msg) { RTopic topic = CLIENT.getTopic(channelKey); topic.publish(msg); } /** * 订阅通道接收消息 * * @param channelKey 通道key * @param clazz 消息类型 * @param consumer 自定义处理 */ public static void subscribe(String channelKey, Class clazz, Consumer consumer) { RTopic topic = CLIENT.getTopic(channelKey); topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */ public static void setCacheObject(final String key, final T value) { setCacheObject(key, value, false); } /** * 缓存基本的对象,保留当前对象 TTL 有效期 * * @param key 缓存的键值 * @param value 缓存的值 * @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90) * @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案 */ public static void setCacheObject(final String key, final T value, final boolean isSaveTtl) { RBucket bucket = CLIENT.getBucket(key); if (isSaveTtl) { try { bucket.setAndKeepTTL(value); } catch (Exception e) { long timeToLive = bucket.remainTimeToLive(); setCacheObject(key, value, Duration.ofMillis(timeToLive)); } } else { bucket.set(value); } } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param duration 时间 */ public static void setCacheObject(final String key, final T value, final Duration duration) { RBatch batch = CLIENT.createBatch(); RBucketAsync bucket = batch.getBucket(key); bucket.setAsync(value); bucket.expireAsync(duration); batch.execute(); } /** * 注册对象监听器 *

* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 * * @param key 缓存的键值 * @param listener 监听器配置 */ public static void addObjectListener(final String key, final ObjectListener listener) { RBucket result = CLIENT.getBucket(key); result.addListener(listener); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */ public static boolean expire(final String key, final long timeout) { return expire(key, Duration.ofSeconds(timeout)); } /** * 设置有效时间 * * @param key Redis键 * @param duration 超时时间 * @return true=设置成功;false=设置失败 */ public static boolean expire(final String key, final Duration duration) { RBucket rBucket = CLIENT.getBucket(key); return rBucket.expire(duration); } /** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */ public static T getCacheObject(final String key) { RBucket rBucket = CLIENT.getBucket(key); return rBucket.get(); } /** * 获得key剩余存活时间 * * @param key 缓存键值 * @return 剩余存活时间 */ public static long getTimeToLive(final String key) { RBucket rBucket = CLIENT.getBucket(key); return rBucket.remainTimeToLive(); } /** * 删除单个对象 * * @param key 缓存的键值 */ public static boolean deleteObject(final String key) { return CLIENT.getBucket(key).delete(); } /** * 删除集合对象 * * @param collection 多个对象 */ public static void deleteObject(final Collection collection) { RBatch batch = CLIENT.createBatch(); collection.forEach(t -> { batch.getBucket(t.toString()).deleteAsync(); }); batch.execute(); } /** * 检查缓存对象是否存在 * * @param key 缓存的键值 */ public static boolean isExistsObject(final String key) { return CLIENT.getBucket(key).isExists(); } /** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public static boolean setCacheList(final String key, final List dataList) { RList rList = CLIENT.getList(key); return rList.addAll(dataList); } /** * 注册List监听器 *

* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 * * @param key 缓存的键值 * @param listener 监听器配置 */ public static void addListListener(final String key, final ObjectListener listener) { RList rList = CLIENT.getList(key); rList.addListener(listener); } /** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */ public static List getCacheList(final String key) { RList rList = CLIENT.getList(key); return rList.readAll(); } /** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ public static boolean setCacheSet(final String key, final Set dataSet) { RSet rSet = CLIENT.getSet(key); return rSet.addAll(dataSet); } /** * 注册Set监听器 *

* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 * * @param key 缓存的键值 * @param listener 监听器配置 */ public static void addSetListener(final String key, final ObjectListener listener) { RSet rSet = CLIENT.getSet(key); rSet.addListener(listener); } /** * 获得缓存的set * * @param key 缓存的key * @return set对象 */ public static Set getCacheSet(final String key) { RSet rSet = CLIENT.getSet(key); return rSet.readAll(); } /** * 缓存Map * * @param key 缓存的键值 * @param dataMap 缓存的数据 */ public static void setCacheMap(final String key, final Map dataMap) { if (dataMap != null) { RMap rMap = CLIENT.getMap(key); rMap.putAll(dataMap); } } /** * 注册Map监听器 *

* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置 * * @param key 缓存的键值 * @param listener 监听器配置 */ public static void addMapListener(final String key, final ObjectListener listener) { RMap rMap = CLIENT.getMap(key); rMap.addListener(listener); } /** * 获得缓存的Map * * @param key 缓存的键值 * @return map对象 */ public static Map getCacheMap(final String key) { RMap rMap = CLIENT.getMap(key); return rMap.getAll(rMap.keySet()); } /** * 获得缓存Map的key列表 * * @param key 缓存的键值 * @return key列表 */ public static Set getCacheMapKeySet(final String key) { RMap rMap = CLIENT.getMap(key); return rMap.keySet(); } /** * 往Hash中存入数据 * * @param key Redis键 * @param hKey Hash键 * @param value 值 */ public static void setCacheMapValue(final String key, final String hKey, final T value) { RMap rMap = CLIENT.getMap(key); rMap.put(hKey, value); } /** * 获取Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */ public static T getCacheMapValue(final String key, final String hKey) { RMap rMap = CLIENT.getMap(key); return rMap.get(hKey); } /** * 删除Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */ public static T delCacheMapValue(final String key, final String hKey) { RMap rMap = CLIENT.getMap(key); return rMap.remove(hKey); } /** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ public static Map getMultiCacheMapValue(final String key, final Set hKeys) { RMap rMap = CLIENT.getMap(key); return rMap.getAll(hKeys); } /** * 设置原子值 * * @param key Redis键 * @param value 值 */ public static void setAtomicValue(String key, long value) { RAtomicLong atomic = CLIENT.getAtomicLong(key); atomic.set(value); } /** * 获取原子值 * * @param key Redis键 * @return 当前值 */ public static long getAtomicValue(String key) { RAtomicLong atomic = CLIENT.getAtomicLong(key); return atomic.get(); } /** * 递增原子值 * * @param key Redis键 * @return 当前值 */ public static long incrAtomicValue(String key) { RAtomicLong atomic = CLIENT.getAtomicLong(key); return atomic.incrementAndGet(); } /** * 递减原子值 * * @param key Redis键 * @return 当前值 */ public static long decrAtomicValue(String key) { RAtomicLong atomic = CLIENT.getAtomicLong(key); return atomic.decrementAndGet(); } /** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */ public static Collection keys(final String pattern) { Stream stream = CLIENT.getKeys().getKeysStreamByPattern(pattern); return stream.collect(Collectors.toList()); } /** * 删除缓存的基本对象列表 * * @param pattern 字符串前缀 */ public static void deleteKeys(final String pattern) { CLIENT.getKeys().deleteByPattern(pattern); } /** * 检查redis中是否存在key * * @param key 键 */ public static Boolean hasKey(String key) { RKeys rKeys = CLIENT.getKeys(); return rKeys.countExists(key) > 0; } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/reflect/ReflectUtils.java ================================================ package top.flya.common.utils.reflect; import cn.hutool.core.util.ReflectUtil; import top.flya.common.utils.StringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.lang.reflect.Method; /** * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. * * @author Lion Li */ @SuppressWarnings("rawtypes") @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ReflectUtils extends ReflectUtil { private static final String SETTER_PREFIX = "set"; private static final String GETTER_PREFIX = "get"; /** * 调用Getter方法. * 支持多级,如:对象名.对象名.方法 */ @SuppressWarnings("unchecked") public static E invokeGetter(Object obj, String propertyName) { Object object = obj; for (String name : StringUtils.split(propertyName, ".")) { String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); object = invoke(object, getterMethodName); } return (E) object; } /** * 调用Setter方法, 仅匹配方法名。 * 支持多级,如:对象名.对象名.方法 */ public static 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 = invoke(object, getterMethodName); } else { String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); Method method = getMethodByName(object.getClass(), setterMethodName); invoke(object, method, value); } } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/spring/SpringUtils.java ================================================ package top.flya.common.utils.spring; import cn.hutool.extra.spring.SpringUtil; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; /** * spring工具类 * * @author Lion Li */ @Component public final class SpringUtils extends SpringUtil { /** * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name) { return getBeanFactory().containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return boolean */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return getBeanFactory().isSingleton(name); } /** * @param name * @return Class 注册对象的类型 */ public static Class getType(String name) throws NoSuchBeanDefinitionException { return getBeanFactory().getType(name); } /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return getBeanFactory().getAliases(name); } /** * 获取aop代理对象 * * @param invoker * @return */ @SuppressWarnings("unchecked") public static T getAopProxy(T invoker) { return (T) AopContext.currentProxy(); } /** * 获取spring上下文 */ public static ApplicationContext context() { return getApplicationContext(); } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/utils/sql/SqlUtil.java ================================================ package top.flya.common.utils.sql; import top.flya.common.exception.UtilException; import top.flya.common.utils.StringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * sql操作工具类 * * @author ruoyi */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class SqlUtil { /** * 定义常用的 sql关键字 */ public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; /** * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) */ public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; /** * 检查字符,防止注入绕过 */ public static String escapeOrderBySql(String value) { //&& !isValidOrderBySql(value) // if (StringUtils.isNotEmpty(value) ) { // 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注入风险"); } } } } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/xss/Xss.java ================================================ package top.flya.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 Lion Li */ @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[] payload() default {}; } ================================================ FILE: ruoyi-common/src/main/java/top/flya/common/xss/XssValidator.java ================================================ package top.flya.common.xss; import cn.hutool.core.util.ReUtil; import cn.hutool.http.HtmlUtil; import lombok.extern.slf4j.Slf4j; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * 自定义xss校验注解实现 * * @author Lion Li */ @Slf4j public class XssValidator implements ConstraintValidator { @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { log.info("xss validator: {}", value); return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value); } } ================================================ FILE: ruoyi-extend/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 ruoyi-extend pom ruoyi-monitor-admin ruoyi-xxl-job-admin ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/Dockerfile ================================================ FROM anapsix/alpine-java:8_server-jre_unlimited MAINTAINER Lion Li RUN mkdir -p /ruoyi/monitor/logs WORKDIR /ruoyi/monitor EXPOSE 9090 ADD ./target/ruoyi-monitor-admin.jar ./app.jar ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/pom.xml ================================================ ruoyi-extend com.ruoyi 4.7.0 4.0.0 jar ruoyi-monitor-admin org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security de.codecentric spring-boot-admin-starter-server de.codecentric spring-boot-admin-starter-client org.projectlombok lombok ${project.artifactId} org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} true repackage ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/MonitorAdminApplication.java ================================================ package top.flya.monitor.admin; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Admin 监控启动程序 * * @author Lion Li */ @SpringBootApplication public class MonitorAdminApplication { public static void main(String[] args) { SpringApplication.run(MonitorAdminApplication.class, args); System.out.println("Admin 监控启动成功"); } } ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/config/AdminServerConfig.java ================================================ package top.flya.monitor.admin.config; import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; import org.springframework.boot.task.TaskExecutorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; /** * springboot-admin server配置类 * * @author Lion Li */ @Configuration @EnableAdminServer public class AdminServerConfig { @Lazy @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) @ConditionalOnMissingBean(Executor.class) public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) { return builder.build(); } } ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/config/SecurityConfig.java ================================================ package top.flya.monitor.admin.config; import de.codecentric.boot.admin.server.config.AdminServerProperties; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; /** * admin 监控 安全配置 * * @author Lion Li */ @EnableWebSecurity public class SecurityConfig { private final String adminContextPath; public SecurityConfig(AdminServerProperties adminServerProperties) { this.adminContextPath = adminServerProperties.getContextPath(); } @Bean public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler(); successHandler.setTargetUrlParameter("redirectTo"); successHandler.setDefaultTargetUrl(adminContextPath + "/"); return httpSecurity .headers().frameOptions().disable() .and().authorizeRequests() .antMatchers(adminContextPath + "/assets/**" , adminContextPath + "/login" , "/actuator" , "/actuator/**" ).permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage(adminContextPath + "/login") .successHandler(successHandler).and() .logout().logoutUrl(adminContextPath + "/logout") .and() .httpBasic().and() .csrf() .disable() .build(); } } ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/src/main/java/top/flya/monitor/admin/notifier/CustomNotifier.java ================================================ package top.flya.monitor.admin.notifier; import de.codecentric.boot.admin.server.domain.entities.Instance; import de.codecentric.boot.admin.server.domain.entities.InstanceRepository; import de.codecentric.boot.admin.server.domain.events.InstanceEvent; import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent; import de.codecentric.boot.admin.server.notify.AbstractEventNotifier; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; /** * 自定义事件通知处理 * * @author Lion Li */ @Slf4j @Component public class CustomNotifier extends AbstractEventNotifier { protected CustomNotifier(InstanceRepository repository) { super(repository); } @Override @SuppressWarnings("all") protected Mono doNotify(InstanceEvent event, Instance instance) { return Mono.fromRunnable(() -> { // 实例状态改变事件 if (event instanceof InstanceStatusChangedEvent) { String registName = instance.getRegistration().getName(); String instanceId = event.getInstance().getValue(); String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(); log.info("Instance Status Change: [{}],[{}],[{}]", registName, instanceId, status); } }); } } ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml ================================================ server: port: 9090 spring: application: name: ruoyi-monitor-admin profiles: active: @profiles.active@ logging: config: classpath:logback-plus.xml --- # 监控中心服务端配置 spring: security: user: name: ruoyi password: 123456 boot: admin: ui: title: RuoYi-Vue-Plus服务监控中心 context-path: /admin --- # Actuator 监控端点的配置项 management: endpoints: web: exposure: include: '*' endpoint: health: show-details: ALWAYS logfile: external-file: ./logs/ruoyi-monitor-admin.log --- # 监控配置 spring.boot.admin.client: # 增加客户端开关 enabled: false # 设置 Spring Boot Admin Server 地址 url: http://localhost:9090/admin instance: service-host-type: IP username: ruoyi password: 123456 ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/src/main/resources/banner.txt ================================================ Application Version: ${ruoyi-vue-plus.version} Spring Boot Version: ${spring-boot.version} __ __ _ _ _ _ | \/ | (_) | /\ | | (_) | \ / | ___ _ __ _| |_ ___ _ __ ______ / \ __| |_ __ ___ _ _ __ | |\/| |/ _ \| '_ \| | __/ _ \| '__|______/ /\ \ / _` | '_ ` _ \| | '_ \ | | | | (_) | | | | | || (_) | | / ____ \ (_| | | | | | | | | | | |_| |_|\___/|_| |_|_|\__\___/|_| /_/ \_\__,_|_| |_| |_|_|_| |_| ================================================ FILE: ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml ================================================ logback ${console.log.pattern} utf-8 ${log.path}.log ${log.path}.%d{yyyy-MM-dd}.log 60 ${log.pattern} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile ================================================ FROM anapsix/alpine-java:8_server-jre_unlimited MAINTAINER Lion Li RUN mkdir -p /ruoyi/xxljob/logs WORKDIR /ruoyi/xxljob ENV TZ=PRC RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone EXPOSE 9100 ADD ./target/ruoyi-xxl-job-admin.jar ./app.jar ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/pom.xml ================================================ 4.0.0 ruoyi-extend com.ruoyi 4.7.0 ruoyi-xxl-job-admin jar org.springframework.boot spring-boot-starter-parent ${spring-boot.version} pom import org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-freemarker org.springframework.boot spring-boot-starter-mail org.springframework.boot spring-boot-starter-actuator org.mybatis.spring.boot mybatis-spring-boot-starter ${spring-boot.mybatis} com.mysql mysql-connector-j de.codecentric spring-boot-admin-starter-client com.xuxueli xxl-job-core ${project.artifactId} org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} repackage ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/XxlJobAdminApplication.java ================================================ package com.xxl.job.admin; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author xuxueli 2018-10-28 00:38:13 */ @SpringBootApplication public class XxlJobAdminApplication { public static void main(String[] args) { SpringApplication.run(XxlJobAdminApplication.class, args); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java ================================================ package com.xxl.job.admin.controller; import com.xxl.job.admin.controller.annotation.PermissionLimit; import com.xxl.job.admin.service.LoginService; import com.xxl.job.admin.service.XxlJobService; import com.xxl.job.core.biz.model.ReturnT; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; /** * index controller * * @author xuxueli 2015-12-19 16:13:16 */ @Controller public class IndexController { @Resource private XxlJobService xxlJobService; @Resource private LoginService loginService; @RequestMapping("/") public String index(Model model) { Map dashboardMap = xxlJobService.dashboardInfo(); model.addAllAttributes(dashboardMap); return "index"; } @RequestMapping("/chartInfo") @ResponseBody public ReturnT> chartInfo(Date startDate, Date endDate) { ReturnT> chartInfo = xxlJobService.chartInfo(startDate, endDate); return chartInfo; } @RequestMapping("/toLogin") @PermissionLimit(limit = false) public ModelAndView toLogin(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) { if (loginService.ifLogin(request, response) != null) { modelAndView.setView(new RedirectView("/", true, false)); return modelAndView; } return new ModelAndView("login"); } @RequestMapping(value = "login", method = RequestMethod.POST) @ResponseBody @PermissionLimit(limit = false) public ReturnT loginDo(HttpServletRequest request, HttpServletResponse response, String userName, String password, String ifRemember) { boolean ifRem = (ifRemember != null && ifRemember.trim().length() > 0 && "on".equals(ifRemember)) ? true : false; return loginService.login(request, response, userName, password, ifRem); } @RequestMapping(value = "logout", method = RequestMethod.POST) @ResponseBody @PermissionLimit(limit = false) public ReturnT logout(HttpServletRequest request, HttpServletResponse response) { return loginService.logout(request, response); } @RequestMapping("/help") public String help() { /*if (!PermissionInterceptor.ifLogin(request)) { return "redirect:/toLogin"; }*/ return "help"; } @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true)); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobApiController.java ================================================ package com.xxl.job.admin.controller; import com.xxl.job.admin.controller.annotation.PermissionLimit; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.core.biz.AdminBiz; import com.xxl.job.core.biz.model.HandleCallbackParam; import com.xxl.job.core.biz.model.RegistryParam; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.util.GsonTool; import com.xxl.job.core.util.XxlJobRemotingUtil; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; /** * Created by xuxueli on 17/5/10. */ @Controller @RequestMapping("/api") public class JobApiController { @Resource private AdminBiz adminBiz; /** * api * * @param uri * @param data * @return */ @RequestMapping("/{uri}") @ResponseBody @PermissionLimit(limit = false) public ReturnT api(HttpServletRequest request, @PathVariable("uri") String uri, @RequestBody(required = false) String data) { // valid if (!"POST".equalsIgnoreCase(request.getMethod())) { return new ReturnT(ReturnT.FAIL_CODE, "invalid request, HttpMethod not support."); } if (uri == null || uri.trim().length() == 0) { return new ReturnT(ReturnT.FAIL_CODE, "invalid request, uri-mapping empty."); } if (XxlJobAdminConfig.getAdminConfig().getAccessToken() != null && XxlJobAdminConfig.getAdminConfig().getAccessToken().trim().length() > 0 && !XxlJobAdminConfig.getAdminConfig().getAccessToken().equals(request.getHeader(XxlJobRemotingUtil.XXL_JOB_ACCESS_TOKEN))) { return new ReturnT(ReturnT.FAIL_CODE, "The access token is wrong."); } // services mapping if ("callback".equals(uri)) { List callbackParamList = GsonTool.fromJson(data, List.class, HandleCallbackParam.class); return adminBiz.callback(callbackParamList); } else if ("registry".equals(uri)) { RegistryParam registryParam = GsonTool.fromJson(data, RegistryParam.class); return adminBiz.registry(registryParam); } else if ("registryRemove".equals(uri)) { RegistryParam registryParam = GsonTool.fromJson(data, RegistryParam.class); return adminBiz.registryRemove(registryParam); } else { return new ReturnT(ReturnT.FAIL_CODE, "invalid request, uri-mapping(" + uri + ") not found."); } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java ================================================ package com.xxl.job.admin.controller; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLogGlue; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogGlueDao; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.glue.GlueTypeEnum; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.List; /** * job code controller * * @author xuxueli 2015-12-19 16:13:16 */ @Controller @RequestMapping("/jobcode") public class JobCodeController { @Resource private XxlJobInfoDao xxlJobInfoDao; @Resource private XxlJobLogGlueDao xxlJobLogGlueDao; @RequestMapping public String index(HttpServletRequest request, Model model, int jobId) { XxlJobInfo jobInfo = xxlJobInfoDao.loadById(jobId); List jobLogGlues = xxlJobLogGlueDao.findByJobId(jobId); if (jobInfo == null) { throw new RuntimeException(I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())) { throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid")); } // valid permission JobInfoController.validPermission(request, jobInfo.getJobGroup()); // Glue类型-字典 model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); model.addAttribute("jobInfo", jobInfo); model.addAttribute("jobLogGlues", jobLogGlues); return "jobcode/jobcode.index"; } @RequestMapping("/save") @ResponseBody public ReturnT save(Model model, int id, String glueSource, String glueRemark) { // valid if (glueRemark == null) { return new ReturnT(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark"))); } if (glueRemark.length() < 4 || glueRemark.length() > 100) { return new ReturnT(500, I18nUtil.getString("jobinfo_glue_remark_limit")); } XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id); if (exists_jobInfo == null) { return new ReturnT(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } // update new code exists_jobInfo.setGlueSource(glueSource); exists_jobInfo.setGlueRemark(glueRemark); exists_jobInfo.setGlueUpdatetime(new Date()); exists_jobInfo.setUpdateTime(new Date()); xxlJobInfoDao.update(exists_jobInfo); // log old code XxlJobLogGlue xxlJobLogGlue = new XxlJobLogGlue(); xxlJobLogGlue.setJobId(exists_jobInfo.getId()); xxlJobLogGlue.setGlueType(exists_jobInfo.getGlueType()); xxlJobLogGlue.setGlueSource(glueSource); xxlJobLogGlue.setGlueRemark(glueRemark); xxlJobLogGlue.setAddTime(new Date()); xxlJobLogGlue.setUpdateTime(new Date()); xxlJobLogGlueDao.save(xxlJobLogGlue); // remove code backup more than 30 xxlJobLogGlueDao.removeOld(exists_jobInfo.getId(), 30); return ReturnT.SUCCESS; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java ================================================ package com.xxl.job.admin.controller; import com.xxl.job.admin.controller.annotation.PermissionLimit; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobRegistry; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobRegistryDao; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.enums.RegistryConfig; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.*; /** * job group controller * * @author xuxueli 2016-10-02 20:52:56 */ @Controller @RequestMapping("/jobgroup") public class JobGroupController { @Resource public XxlJobInfoDao xxlJobInfoDao; @Resource public XxlJobGroupDao xxlJobGroupDao; @Resource private XxlJobRegistryDao xxlJobRegistryDao; @RequestMapping @PermissionLimit(adminuser = true) public String index(Model model) { return "jobgroup/jobgroup.index"; } @RequestMapping("/pageList") @ResponseBody @PermissionLimit(adminuser = true) public Map pageList(HttpServletRequest request, @RequestParam(required = false, defaultValue = "0") int start, @RequestParam(required = false, defaultValue = "10") int length, String appname, String title) { // page query List list = xxlJobGroupDao.pageList(start, length, appname, title); int list_count = xxlJobGroupDao.pageListCount(start, length, appname, title); // package result Map maps = new HashMap(); maps.put("recordsTotal", list_count); // 总记录数 maps.put("recordsFiltered", list_count); // 过滤后的总记录数 maps.put("data", list); // 分页列表 return maps; } @RequestMapping("/save") @ResponseBody @PermissionLimit(adminuser = true) public ReturnT save(XxlJobGroup xxlJobGroup) { // valid if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) { return new ReturnT(500, (I18nUtil.getString("system_please_input") + "AppName")); } if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_appname_length")); } if (xxlJobGroup.getAppname().contains(">") || xxlJobGroup.getAppname().contains("<")) { return new ReturnT(500, "AppName" + I18nUtil.getString("system_unvalid")); } if (xxlJobGroup.getTitle() == null || xxlJobGroup.getTitle().trim().length() == 0) { return new ReturnT(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title"))); } if (xxlJobGroup.getTitle().contains(">") || xxlJobGroup.getTitle().contains("<")) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_title") + I18nUtil.getString("system_unvalid")); } if (xxlJobGroup.getAddressType() != 0) { if (xxlJobGroup.getAddressList() == null || xxlJobGroup.getAddressList().trim().length() == 0) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_addressType_limit")); } if (xxlJobGroup.getAddressList().contains(">") || xxlJobGroup.getAddressList().contains("<")) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_registryList") + I18nUtil.getString("system_unvalid")); } String[] addresss = xxlJobGroup.getAddressList().split(","); for (String item : addresss) { if (item == null || item.trim().length() == 0) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_registryList_unvalid")); } } } // process xxlJobGroup.setUpdateTime(new Date()); int ret = xxlJobGroupDao.save(xxlJobGroup); return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL; } @RequestMapping("/update") @ResponseBody @PermissionLimit(adminuser = true) public ReturnT update(XxlJobGroup xxlJobGroup) { // valid if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) { return new ReturnT(500, (I18nUtil.getString("system_please_input") + "AppName")); } if (xxlJobGroup.getAppname().length() < 4 || xxlJobGroup.getAppname().length() > 64) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_appname_length")); } if (xxlJobGroup.getTitle() == null || xxlJobGroup.getTitle().trim().length() == 0) { return new ReturnT(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title"))); } if (xxlJobGroup.getAddressType() == 0) { // 0=自动注册 List registryList = findRegistryByAppName(xxlJobGroup.getAppname()); String addressListStr = null; if (registryList != null && !registryList.isEmpty()) { Collections.sort(registryList); addressListStr = ""; for (String item : registryList) { addressListStr += item + ","; } addressListStr = addressListStr.substring(0, addressListStr.length() - 1); } xxlJobGroup.setAddressList(addressListStr); } else { // 1=手动录入 if (xxlJobGroup.getAddressList() == null || xxlJobGroup.getAddressList().trim().length() == 0) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_addressType_limit")); } String[] addresss = xxlJobGroup.getAddressList().split(","); for (String item : addresss) { if (item == null || item.trim().length() == 0) { return new ReturnT(500, I18nUtil.getString("jobgroup_field_registryList_unvalid")); } } } // process xxlJobGroup.setUpdateTime(new Date()); int ret = xxlJobGroupDao.update(xxlJobGroup); return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL; } private List findRegistryByAppName(String appnameParam) { HashMap> appAddressMap = new HashMap>(); List list = xxlJobRegistryDao.findAll(RegistryConfig.DEAD_TIMEOUT, new Date()); if (list != null) { for (XxlJobRegistry item : list) { if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) { String appname = item.getRegistryKey(); List registryList = appAddressMap.get(appname); if (registryList == null) { registryList = new ArrayList(); } if (!registryList.contains(item.getRegistryValue())) { registryList.add(item.getRegistryValue()); } appAddressMap.put(appname, registryList); } } } return appAddressMap.get(appnameParam); } @RequestMapping("/remove") @ResponseBody @PermissionLimit(adminuser = true) public ReturnT remove(int id) { // valid int count = xxlJobInfoDao.pageListCount(0, 10, id, -1, null, null, null); if (count > 0) { return new ReturnT(500, I18nUtil.getString("jobgroup_del_limit_0")); } List allList = xxlJobGroupDao.findAll(); if (allList.size() == 1) { return new ReturnT(500, I18nUtil.getString("jobgroup_del_limit_1")); } int ret = xxlJobGroupDao.remove(id); return (ret > 0) ? ReturnT.SUCCESS : ReturnT.FAIL; } @RequestMapping("/loadById") @ResponseBody @PermissionLimit(adminuser = true) public ReturnT loadById(int id) { XxlJobGroup jobGroup = xxlJobGroupDao.load(id); return jobGroup != null ? new ReturnT(jobGroup) : new ReturnT(ReturnT.FAIL_CODE, null); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java ================================================ package com.xxl.job.admin.controller; import com.xxl.job.admin.core.exception.XxlJobException; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobUser; import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.core.thread.JobScheduleHelper; import com.xxl.job.admin.core.thread.JobTriggerPoolHelper; import com.xxl.job.admin.core.trigger.TriggerTypeEnum; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.service.LoginService; import com.xxl.job.admin.service.XxlJobService; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.util.DateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.*; /** * index controller * * @author xuxueli 2015-12-19 16:13:16 */ @Controller @RequestMapping("/jobinfo") public class JobInfoController { private static Logger logger = LoggerFactory.getLogger(JobInfoController.class); @Resource private XxlJobGroupDao xxlJobGroupDao; @Resource private XxlJobService xxlJobService; @RequestMapping public String index(HttpServletRequest request, Model model, @RequestParam(required = false, defaultValue = "-1") int jobGroup) { // 枚举-字典 model.addAttribute("ExecutorRouteStrategyEnum", ExecutorRouteStrategyEnum.values()); // 路由策略-列表 model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); // Glue类型-字典 model.addAttribute("ExecutorBlockStrategyEnum", ExecutorBlockStrategyEnum.values()); // 阻塞处理策略-字典 model.addAttribute("ScheduleTypeEnum", ScheduleTypeEnum.values()); // 调度类型 model.addAttribute("MisfireStrategyEnum", MisfireStrategyEnum.values()); // 调度过期策略 // 执行器列表 List jobGroupList_all = xxlJobGroupDao.findAll(); // filter group List jobGroupList = filterJobGroupByRole(request, jobGroupList_all); if (jobGroupList == null || jobGroupList.size() == 0) { throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); } model.addAttribute("JobGroupList", jobGroupList); model.addAttribute("jobGroup", jobGroup); return "jobinfo/jobinfo.index"; } public static List filterJobGroupByRole(HttpServletRequest request, List jobGroupList_all) { List jobGroupList = new ArrayList<>(); if (jobGroupList_all != null && jobGroupList_all.size() > 0) { XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); if (loginUser.getRole() == 1) { jobGroupList = jobGroupList_all; } else { List groupIdStrs = new ArrayList<>(); if (loginUser.getPermission() != null && loginUser.getPermission().trim().length() > 0) { groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(",")); } for (XxlJobGroup groupItem : jobGroupList_all) { if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) { jobGroupList.add(groupItem); } } } } return jobGroupList; } public static void validPermission(HttpServletRequest request, int jobGroup) { XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); if (!loginUser.validPermission(jobGroup)) { throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username=" + loginUser.getUsername() + "]"); } } @RequestMapping("/pageList") @ResponseBody public Map pageList(@RequestParam(required = false, defaultValue = "0") int start, @RequestParam(required = false, defaultValue = "10") int length, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) { return xxlJobService.pageList(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author); } @RequestMapping("/add") @ResponseBody public ReturnT add(XxlJobInfo jobInfo) { return xxlJobService.add(jobInfo); } @RequestMapping("/update") @ResponseBody public ReturnT update(XxlJobInfo jobInfo) { return xxlJobService.update(jobInfo); } @RequestMapping("/remove") @ResponseBody public ReturnT remove(int id) { return xxlJobService.remove(id); } @RequestMapping("/stop") @ResponseBody public ReturnT pause(int id) { return xxlJobService.stop(id); } @RequestMapping("/start") @ResponseBody public ReturnT start(int id) { return xxlJobService.start(id); } @RequestMapping("/trigger") @ResponseBody //@PermissionLimit(limit = false) public ReturnT triggerJob(int id, String executorParam, String addressList) { // force cover job param if (executorParam == null) { executorParam = ""; } JobTriggerPoolHelper.trigger(id, TriggerTypeEnum.MANUAL, -1, null, executorParam, addressList); return ReturnT.SUCCESS; } @RequestMapping("/nextTriggerTime") @ResponseBody public ReturnT> nextTriggerTime(String scheduleType, String scheduleConf) { XxlJobInfo paramXxlJobInfo = new XxlJobInfo(); paramXxlJobInfo.setScheduleType(scheduleType); paramXxlJobInfo.setScheduleConf(scheduleConf); List result = new ArrayList<>(); try { Date lastTime = new Date(); for (int i = 0; i < 5; i++) { lastTime = JobScheduleHelper.generateNextValidTime(paramXxlJobInfo, lastTime); if (lastTime != null) { result.add(DateUtil.formatDateTime(lastTime)); } else { break; } } } catch (Exception e) { logger.error(e.getMessage(), e); return new ReturnT>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid")) + e.getMessage()); } return new ReturnT>(result); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java ================================================ package com.xxl.job.admin.controller; import com.xxl.job.admin.core.exception.XxlJobException; import com.xxl.job.admin.core.complete.XxlJobCompleter; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.scheduler.XxlJobScheduler; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogDao; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.KillParam; import com.xxl.job.core.biz.model.LogParam; import com.xxl.job.core.biz.model.LogResult; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.util.DateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * index controller * * @author xuxueli 2015-12-19 16:13:16 */ @Controller @RequestMapping("/joblog") public class JobLogController { private static Logger logger = LoggerFactory.getLogger(JobLogController.class); @Resource private XxlJobGroupDao xxlJobGroupDao; @Resource public XxlJobInfoDao xxlJobInfoDao; @Resource public XxlJobLogDao xxlJobLogDao; @RequestMapping public String index(HttpServletRequest request, Model model, @RequestParam(required = false, defaultValue = "0") Integer jobId) { // 执行器列表 List jobGroupList_all = xxlJobGroupDao.findAll(); // filter group List jobGroupList = JobInfoController.filterJobGroupByRole(request, jobGroupList_all); if (jobGroupList == null || jobGroupList.size() == 0) { throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); } model.addAttribute("JobGroupList", jobGroupList); // 任务 if (jobId > 0) { XxlJobInfo jobInfo = xxlJobInfoDao.loadById(jobId); if (jobInfo == null) { throw new RuntimeException(I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_unvalid")); } model.addAttribute("jobInfo", jobInfo); // valid permission JobInfoController.validPermission(request, jobInfo.getJobGroup()); } return "joblog/joblog.index"; } @RequestMapping("/getJobsByGroup") @ResponseBody public ReturnT> getJobsByGroup(int jobGroup) { List list = xxlJobInfoDao.getJobsByGroup(jobGroup); return new ReturnT>(list); } @RequestMapping("/pageList") @ResponseBody public Map pageList(HttpServletRequest request, @RequestParam(required = false, defaultValue = "0") int start, @RequestParam(required = false, defaultValue = "10") int length, int jobGroup, int jobId, int logStatus, String filterTime) { // valid permission JobInfoController.validPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup // parse param Date triggerTimeStart = null; Date triggerTimeEnd = null; if (filterTime != null && filterTime.trim().length() > 0) { String[] temp = filterTime.split(" - "); if (temp.length == 2) { triggerTimeStart = DateUtil.parseDateTime(temp[0]); triggerTimeEnd = DateUtil.parseDateTime(temp[1]); } } // page query List list = xxlJobLogDao.pageList(start, length, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus); int list_count = xxlJobLogDao.pageListCount(start, length, jobGroup, jobId, triggerTimeStart, triggerTimeEnd, logStatus); // package result Map maps = new HashMap(); maps.put("recordsTotal", list_count); // 总记录数 maps.put("recordsFiltered", list_count); // 过滤后的总记录数 maps.put("data", list); // 分页列表 return maps; } @RequestMapping("/logDetailPage") public String logDetailPage(int id, Model model) { // base check ReturnT logStatue = ReturnT.SUCCESS; XxlJobLog jobLog = xxlJobLogDao.load(id); if (jobLog == null) { throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid")); } model.addAttribute("triggerCode", jobLog.getTriggerCode()); model.addAttribute("handleCode", jobLog.getHandleCode()); model.addAttribute("logId", jobLog.getId()); return "joblog/joblog.detail"; } @RequestMapping("/logDetailCat") @ResponseBody public ReturnT logDetailCat(long logId, int fromLineNum) { try { // valid XxlJobLog jobLog = xxlJobLogDao.load(logId); // todo, need to improve performance if (jobLog == null) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_logid_unvalid")); } // log cat ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress()); ReturnT logResult = executorBiz.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum)); // is end if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) { if (jobLog.getHandleCode() > 0) { logResult.getContent().setEnd(true); } } return logResult; } catch (Exception e) { logger.error(e.getMessage(), e); return new ReturnT(ReturnT.FAIL_CODE, e.getMessage()); } } @RequestMapping("/logKill") @ResponseBody public ReturnT logKill(int id) { // base check XxlJobLog log = xxlJobLogDao.load(id); XxlJobInfo jobInfo = xxlJobInfoDao.loadById(log.getJobId()); if (jobInfo == null) { return new ReturnT(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) { return new ReturnT(500, I18nUtil.getString("joblog_kill_log_limit")); } // request of kill ReturnT runResult = null; try { ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(log.getExecutorAddress()); runResult = executorBiz.kill(new KillParam(jobInfo.getId())); } catch (Exception e) { logger.error(e.getMessage(), e); runResult = new ReturnT(500, e.getMessage()); } if (ReturnT.SUCCESS_CODE == runResult.getCode()) { log.setHandleCode(ReturnT.FAIL_CODE); log.setHandleMsg(I18nUtil.getString("joblog_kill_log_byman") + ":" + (runResult.getMsg() != null ? runResult.getMsg() : "")); log.setHandleTime(new Date()); XxlJobCompleter.updateHandleInfoAndFinish(log); return new ReturnT(runResult.getMsg()); } else { return new ReturnT(500, runResult.getMsg()); } } @RequestMapping("/clearLog") @ResponseBody public ReturnT clearLog(int jobGroup, int jobId, int type) { Date clearBeforeTime = null; int clearBeforeNum = 0; if (type == 1) { clearBeforeTime = DateUtil.addMonths(new Date(), -1); // 清理一个月之前日志数据 } else if (type == 2) { clearBeforeTime = DateUtil.addMonths(new Date(), -3); // 清理三个月之前日志数据 } else if (type == 3) { clearBeforeTime = DateUtil.addMonths(new Date(), -6); // 清理六个月之前日志数据 } else if (type == 4) { clearBeforeTime = DateUtil.addYears(new Date(), -1); // 清理一年之前日志数据 } else if (type == 5) { clearBeforeNum = 1000; // 清理一千条以前日志数据 } else if (type == 6) { clearBeforeNum = 10000; // 清理一万条以前日志数据 } else if (type == 7) { clearBeforeNum = 30000; // 清理三万条以前日志数据 } else if (type == 8) { clearBeforeNum = 100000; // 清理十万条以前日志数据 } else if (type == 9) { clearBeforeNum = 0; // 清理所有日志数据 } else { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_clean_type_unvalid")); } List logIds = null; do { logIds = xxlJobLogDao.findClearLogIds(jobGroup, jobId, clearBeforeTime, clearBeforeNum, 1000); if (logIds != null && logIds.size() > 0) { xxlJobLogDao.clearLog(logIds); } } while (logIds != null && logIds.size() > 0); return ReturnT.SUCCESS; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/UserController.java ================================================ package com.xxl.job.admin.controller; import com.xxl.job.admin.controller.annotation.PermissionLimit; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobUser; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobUserDao; import com.xxl.job.admin.service.LoginService; import com.xxl.job.core.biz.model.ReturnT; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.DigestUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author xuxueli 2019-05-04 16:39:50 */ @Controller @RequestMapping("/user") public class UserController { @Resource private XxlJobUserDao xxlJobUserDao; @Resource private XxlJobGroupDao xxlJobGroupDao; @RequestMapping @PermissionLimit(adminuser = true) public String index(Model model) { // 执行器列表 List groupList = xxlJobGroupDao.findAll(); model.addAttribute("groupList", groupList); return "user/user.index"; } @RequestMapping("/pageList") @ResponseBody @PermissionLimit(adminuser = true) public Map pageList(@RequestParam(required = false, defaultValue = "0") int start, @RequestParam(required = false, defaultValue = "10") int length, String username, int role) { // page list List list = xxlJobUserDao.pageList(start, length, username, role); int list_count = xxlJobUserDao.pageListCount(start, length, username, role); // filter if (list != null && list.size() > 0) { for (XxlJobUser item : list) { item.setPassword(null); } } // package result Map maps = new HashMap(); maps.put("recordsTotal", list_count); // 总记录数 maps.put("recordsFiltered", list_count); // 过滤后的总记录数 maps.put("data", list); // 分页列表 return maps; } @RequestMapping("/add") @ResponseBody @PermissionLimit(adminuser = true) public ReturnT add(XxlJobUser xxlJobUser) { // valid username if (!StringUtils.hasText(xxlJobUser.getUsername())) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("system_please_input") + I18nUtil.getString("user_username")); } xxlJobUser.setUsername(xxlJobUser.getUsername().trim()); if (!(xxlJobUser.getUsername().length() >= 4 && xxlJobUser.getUsername().length() <= 20)) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]"); } // valid password if (!StringUtils.hasText(xxlJobUser.getPassword())) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("system_please_input") + I18nUtil.getString("user_password")); } xxlJobUser.setPassword(xxlJobUser.getPassword().trim()); if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]"); } // md5 password xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes())); // check repeat XxlJobUser existUser = xxlJobUserDao.loadByUserName(xxlJobUser.getUsername()); if (existUser != null) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("user_username_repeat")); } // write xxlJobUserDao.save(xxlJobUser); return ReturnT.SUCCESS; } @RequestMapping("/update") @ResponseBody @PermissionLimit(adminuser = true) public ReturnT update(HttpServletRequest request, XxlJobUser xxlJobUser) { // avoid opt login seft XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); if (loginUser.getUsername().equals(xxlJobUser.getUsername())) { return new ReturnT(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit")); } // valid password if (StringUtils.hasText(xxlJobUser.getPassword())) { xxlJobUser.setPassword(xxlJobUser.getPassword().trim()); if (!(xxlJobUser.getPassword().length() >= 4 && xxlJobUser.getPassword().length() <= 20)) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]"); } // md5 password xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes())); } else { xxlJobUser.setPassword(null); } // write xxlJobUserDao.update(xxlJobUser); return ReturnT.SUCCESS; } @RequestMapping("/remove") @ResponseBody @PermissionLimit(adminuser = true) public ReturnT remove(HttpServletRequest request, int id) { // avoid opt login seft XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); if (loginUser.getId() == id) { return new ReturnT(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit")); } xxlJobUserDao.delete(id); return ReturnT.SUCCESS; } @RequestMapping("/updatePwd") @ResponseBody public ReturnT updatePwd(HttpServletRequest request, String password) { // valid password if (password == null || password.trim().length() == 0) { return new ReturnT(ReturnT.FAIL.getCode(), "密码不可为空"); } password = password.trim(); if (!(password.length() >= 4 && password.length() <= 20)) { return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit") + "[4-20]"); } // md5 password String md5Password = DigestUtils.md5DigestAsHex(password.getBytes()); // update pwd XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY); // do write XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername()); existUser.setPassword(md5Password); xxlJobUserDao.update(existUser); return ReturnT.SUCCESS; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/annotation/PermissionLimit.java ================================================ package com.xxl.job.admin.controller.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 权限限制 * * @author xuxueli 2015-12-12 18:29:02 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface PermissionLimit { /** * 登录拦截 (默认拦截) */ boolean limit() default true; /** * 要求管理员权限 * * @return */ boolean adminuser() default false; } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java ================================================ package com.xxl.job.admin.controller.interceptor; import com.xxl.job.admin.core.util.FtlUtil; import com.xxl.job.admin.core.util.I18nUtil; import org.springframework.stereotype.Component; import org.springframework.web.servlet.AsyncHandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; /** * push cookies to model as cookieMap * * @author xuxueli 2015-12-12 18:09:04 */ @Component public class CookieInterceptor implements AsyncHandlerInterceptor { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // cookie if (modelAndView != null && request.getCookies() != null && request.getCookies().length > 0) { HashMap cookieMap = new HashMap(); for (Cookie ck : request.getCookies()) { cookieMap.put(ck.getName(), ck); } modelAndView.addObject("cookieMap", cookieMap); } // static method if (modelAndView != null) { modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName())); } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java ================================================ package com.xxl.job.admin.controller.interceptor; import com.xxl.job.admin.controller.annotation.PermissionLimit; import com.xxl.job.admin.core.model.XxlJobUser; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.service.LoginService; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.AsyncHandlerInterceptor; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 权限拦截 * * @author xuxueli 2015-12-12 18:09:04 */ @Component public class PermissionInterceptor implements AsyncHandlerInterceptor { @Resource private LoginService loginService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; // proceed with the next interceptor } // if need login boolean needLogin = true; boolean needAdminuser = false; HandlerMethod method = (HandlerMethod) handler; PermissionLimit permission = method.getMethodAnnotation(PermissionLimit.class); if (permission != null) { needLogin = permission.limit(); needAdminuser = permission.adminuser(); } if (needLogin) { XxlJobUser loginUser = loginService.ifLogin(request, response); if (loginUser == null) { response.setStatus(302); response.setHeader("location", request.getContextPath() + "/toLogin"); return false; } if (needAdminuser && loginUser.getRole() != 1) { throw new RuntimeException(I18nUtil.getString("system_permission_limit")); } request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser); } return true; // proceed with the next interceptor } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/WebMvcConfig.java ================================================ package com.xxl.job.admin.controller.interceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; /** * web mvc config * * @author xuxueli 2018-04-02 20:48:20 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Resource private PermissionInterceptor permissionInterceptor; @Resource private CookieInterceptor cookieInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(permissionInterceptor).addPathPatterns("/**"); registry.addInterceptor(cookieInterceptor).addPathPatterns("/**"); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java ================================================ package com.xxl.job.admin.controller.resolver; import com.xxl.job.admin.core.exception.XxlJobException; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.admin.core.util.JacksonUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * common exception resolver * * @author xuxueli 2016-1-6 19:22:18 */ @Component public class WebExceptionResolver implements HandlerExceptionResolver { private static transient Logger logger = LoggerFactory.getLogger(WebExceptionResolver.class); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (!(ex instanceof XxlJobException)) { logger.error("WebExceptionResolver:{}", ex); } // if json boolean isJson = false; if (handler instanceof HandlerMethod) { HandlerMethod method = (HandlerMethod) handler; ResponseBody responseBody = method.getMethodAnnotation(ResponseBody.class); if (responseBody != null) { isJson = true; } } // error result ReturnT errorResult = new ReturnT(ReturnT.FAIL_CODE, ex.toString().replaceAll("\n", "
")); // response ModelAndView mv = new ModelAndView(); if (isJson) { try { response.setContentType("application/json;charset=utf-8"); response.getWriter().print(JacksonUtil.writeValueAsString(errorResult)); } catch (IOException e) { logger.error(e.getMessage(), e); } return mv; } else { mv.addObject("exceptionMsg", errorResult.getMsg()); mv.setViewName("/common/common.exception"); return mv; } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarm.java ================================================ package com.xxl.job.admin.core.alarm; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; /** * @author xuxueli 2020-01-19 */ public interface JobAlarm { /** * job alarm * * @param info * @param jobLog * @return */ public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java ================================================ package com.xxl.job.admin.core.alarm; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Map; @Component public class JobAlarmer implements ApplicationContextAware, InitializingBean { private static Logger logger = LoggerFactory.getLogger(JobAlarmer.class); private ApplicationContext applicationContext; private List jobAlarmList; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() throws Exception { Map serviceBeanMap = applicationContext.getBeansOfType(JobAlarm.class); if (serviceBeanMap != null && serviceBeanMap.size() > 0) { jobAlarmList = new ArrayList(serviceBeanMap.values()); } } /** * job alarm * * @param info * @param jobLog * @return */ public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) { boolean result = false; if (jobAlarmList != null && jobAlarmList.size() > 0) { result = true; // success means all-success for (JobAlarm alarm : jobAlarmList) { boolean resultItem = false; try { resultItem = alarm.doAlarm(info, jobLog); } catch (Exception e) { logger.error(e.getMessage(), e); } if (!resultItem) { result = false; } } } return result; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java ================================================ package com.xxl.job.admin.core.alarm.impl; import com.xxl.job.admin.core.alarm.JobAlarm; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.model.ReturnT; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component; import javax.mail.internet.MimeMessage; import java.text.MessageFormat; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * job alarm by email * * @author xuxueli 2020-01-19 */ @Component public class EmailJobAlarm implements JobAlarm { private static Logger logger = LoggerFactory.getLogger(EmailJobAlarm.class); /** * fail alarm * * @param jobLog */ @Override public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) { boolean alarmResult = true; // send monitor email if (info != null && info.getAlarmEmail() != null && info.getAlarmEmail().trim().length() > 0) { // alarmContent String alarmContent = "Alarm Job LogId=" + jobLog.getId(); if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) { alarmContent += "
TriggerMsg=
" + jobLog.getTriggerMsg(); } if (jobLog.getHandleCode() > 0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) { alarmContent += "
HandleCode=" + jobLog.getHandleMsg(); } // email info XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup())); String personal = I18nUtil.getString("admin_name_full"); String title = I18nUtil.getString("jobconf_monitor"); String content = MessageFormat.format(loadEmailJobAlarmTemplate(), group != null ? group.getTitle() : "null", info.getId(), info.getJobDesc(), alarmContent); Set emailSet = new HashSet(Arrays.asList(info.getAlarmEmail().split(","))); for (String email : emailSet) { // make mail try { MimeMessage mimeMessage = XxlJobAdminConfig.getAdminConfig().getMailSender().createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); helper.setFrom(XxlJobAdminConfig.getAdminConfig().getEmailFrom(), personal); helper.setTo(email); helper.setSubject(title); helper.setText(content, true); XxlJobAdminConfig.getAdminConfig().getMailSender().send(mimeMessage); } catch (Exception e) { logger.error(">>>>>>>>>>> xxl-job, job fail alarm email send error, JobLogId:{}", jobLog.getId(), e); alarmResult = false; } } } return alarmResult; } /** * load email job alarm template * * @return */ private static final String loadEmailJobAlarmTemplate() { String mailBodyTemplate = "

" + I18nUtil.getString("jobconf_monitor_detail") + ":" + "\n" + " " + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
" + I18nUtil.getString("jobinfo_field_jobgroup") + "" + I18nUtil.getString("jobinfo_field_id") + "" + I18nUtil.getString("jobinfo_field_jobdesc") + "" + I18nUtil.getString("jobconf_monitor_alarm_title") + "" + I18nUtil.getString("jobconf_monitor_alarm_content") + "
{0}{1}{2}" + I18nUtil.getString("jobconf_monitor_alarm_type") + "{3}
"; return mailBodyTemplate; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java ================================================ package com.xxl.job.admin.core.complete; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.thread.JobTriggerPoolHelper; import com.xxl.job.admin.core.trigger.TriggerTypeEnum; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.context.XxlJobContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.MessageFormat; /** * @author xuxueli 2020-10-30 20:43:10 */ public class XxlJobCompleter { private static Logger logger = LoggerFactory.getLogger(XxlJobCompleter.class); /** * common fresh handle entrance (limit only once) * * @param xxlJobLog * @return */ public static int updateHandleInfoAndFinish(XxlJobLog xxlJobLog) { // finish finishJob(xxlJobLog); // text最大64kb 避免长度过长 if (xxlJobLog.getHandleMsg().length() > 15000) { xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 15000)); } // fresh handle return XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateHandleInfo(xxlJobLog); } /** * do somethind to finish job */ private static void finishJob(XxlJobLog xxlJobLog) { // 1、handle success, to trigger child job String triggerChildMsg = null; if (XxlJobContext.HANDLE_CODE_SUCCESS == xxlJobLog.getHandleCode()) { XxlJobInfo xxlJobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(xxlJobLog.getJobId()); if (xxlJobInfo != null && xxlJobInfo.getChildJobId() != null && xxlJobInfo.getChildJobId().trim().length() > 0) { triggerChildMsg = "

>>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_child_run") + "<<<<<<<<<<<
"; String[] childJobIds = xxlJobInfo.getChildJobId().split(","); for (int i = 0; i < childJobIds.length; i++) { int childJobId = (childJobIds[i] != null && childJobIds[i].trim().length() > 0 && isNumeric(childJobIds[i])) ? Integer.valueOf(childJobIds[i]) : -1; if (childJobId > 0) { JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null); ReturnT triggerChildResult = ReturnT.SUCCESS; // add msg triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"), (i + 1), childJobIds.length, childJobIds[i], (triggerChildResult.getCode() == ReturnT.SUCCESS_CODE ? I18nUtil.getString("system_success") : I18nUtil.getString("system_fail")), triggerChildResult.getMsg()); } else { triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"), (i + 1), childJobIds.length, childJobIds[i]); } } } } if (triggerChildMsg != null) { xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg() + triggerChildMsg); } // 2、fix_delay trigger next // on the way } private static boolean isNumeric(String str) { try { int result = Integer.valueOf(str); return true; } catch (NumberFormatException e) { return false; } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java ================================================ package com.xxl.job.admin.core.conf; import com.xxl.job.admin.core.alarm.JobAlarmer; import com.xxl.job.admin.core.scheduler.XxlJobScheduler; import com.xxl.job.admin.dao.*; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Arrays; /** * xxl-job config * * @author xuxueli 2017-04-28 */ @Component public class XxlJobAdminConfig implements InitializingBean, DisposableBean { private static XxlJobAdminConfig adminConfig = null; public static XxlJobAdminConfig getAdminConfig() { return adminConfig; } // ---------------------- XxlJobScheduler ---------------------- private XxlJobScheduler xxlJobScheduler; @Override public void afterPropertiesSet() throws Exception { adminConfig = this; xxlJobScheduler = new XxlJobScheduler(); xxlJobScheduler.init(); } @Override public void destroy() throws Exception { xxlJobScheduler.destroy(); } // ---------------------- XxlJobScheduler ---------------------- // conf @Value("${xxl.job.i18n}") private String i18n; @Value("${xxl.job.accessToken}") private String accessToken; @Value("${spring.mail.from}") private String emailFrom; @Value("${xxl.job.triggerpool.fast.max}") private int triggerPoolFastMax; @Value("${xxl.job.triggerpool.slow.max}") private int triggerPoolSlowMax; @Value("${xxl.job.logretentiondays}") private int logretentiondays; // dao, service @Resource private XxlJobLogDao xxlJobLogDao; @Resource private XxlJobInfoDao xxlJobInfoDao; @Resource private XxlJobRegistryDao xxlJobRegistryDao; @Resource private XxlJobGroupDao xxlJobGroupDao; @Resource private XxlJobLogReportDao xxlJobLogReportDao; @Resource private JavaMailSender mailSender; @Resource private DataSource dataSource; @Resource private JobAlarmer jobAlarmer; public String getI18n() { if (!Arrays.asList("zh_CN", "zh_TC", "en").contains(i18n)) { return "zh_CN"; } return i18n; } public String getAccessToken() { return accessToken; } public String getEmailFrom() { return emailFrom; } public int getTriggerPoolFastMax() { if (triggerPoolFastMax < 200) { return 200; } return triggerPoolFastMax; } public int getTriggerPoolSlowMax() { if (triggerPoolSlowMax < 100) { return 100; } return triggerPoolSlowMax; } public int getLogretentiondays() { if (logretentiondays < 7) { return -1; // Limit greater than or equal to 7, otherwise close } return logretentiondays; } public XxlJobLogDao getXxlJobLogDao() { return xxlJobLogDao; } public XxlJobInfoDao getXxlJobInfoDao() { return xxlJobInfoDao; } public XxlJobRegistryDao getXxlJobRegistryDao() { return xxlJobRegistryDao; } public XxlJobGroupDao getXxlJobGroupDao() { return xxlJobGroupDao; } public XxlJobLogReportDao getXxlJobLogReportDao() { return xxlJobLogReportDao; } public JavaMailSender getMailSender() { return mailSender; } public DataSource getDataSource() { return dataSource; } public JobAlarmer getJobAlarmer() { return jobAlarmer; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java ================================================ /* * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package com.xxl.job.admin.core.cron; import java.io.Serializable; import java.text.ParseException; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TimeZone; import java.util.TreeSet; /** * Provides a parser and evaluator for unix-like cron expressions. Cron * expressions provide the ability to specify complex time combinations such as * "At 8:00am every Monday through Friday" or "At 1:30am every * last Friday of the month". *

* Cron expressions are comprised of 6 required fields and one optional field * separated by white space. The fields respectively are described as follows: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Field Name Allowed Values Allowed Special Characters
Seconds  * 0-59  * , - * /
Minutes  * 0-59  * , - * /
Hours  * 0-23  * , - * /
Day-of-month  * 1-31  * , - * ? / L W
Month  * 0-11 or JAN-DEC  * , - * /
Day-of-Week  * 1-7 or SUN-SAT  * , - * ? / L #
Year (Optional)  * empty, 1970-2199  * , - * /
*

* The '*' character is used to specify all values. For example, "*" * in the minute field means "every minute". *

* The '?' character is allowed for the day-of-month and day-of-week fields. It * is used to specify 'no specific value'. This is useful when you need to * specify something in one of the two fields, but not the other. *

* The '-' character is used to specify ranges For example "10-12" in * the hour field means "the hours 10, 11 and 12". *

* The ',' character is used to specify additional values. For example * "MON,WED,FRI" in the day-of-week field means "the days Monday, * Wednesday, and Friday". *

* The '/' character is used to specify increments. For example "0/15" * in the seconds field means "the seconds 0, 15, 30, and 45". And * "5/15" in the seconds field means "the seconds 5, 20, 35, and * 50". Specifying '*' before the '/' is equivalent to specifying 0 is * the value to start with. Essentially, for each field in the expression, there * is a set of numbers that can be turned on or off. For seconds and minutes, * the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to * 31, and for months 0 to 11 (JAN to DEC). The "/" character simply helps you turn * on every "nth" value in the given set. Thus "7/6" in the * month field only turns on month "7", it does NOT mean every 6th * month, please note that subtlety. *

* The 'L' character is allowed for the day-of-month and day-of-week fields. * This character is short-hand for "last", but it has different * meaning in each of the two fields. For example, the value "L" in * the day-of-month field means "the last day of the month" - day 31 * for January, day 28 for February on non-leap years. If used in the * day-of-week field by itself, it simply means "7" or * "SAT". But if used in the day-of-week field after another value, it * means "the last xxx day of the month" - for example "6L" * means "the last friday of the month". You can also specify an offset * from the last day of the month, such as "L-3" which would mean the third-to-last * day of the calendar month. When using the 'L' option, it is important not to * specify lists, or ranges of values, as you'll get confusing/unexpected results. *

* The 'W' character is allowed for the day-of-month field. This character * is used to specify the weekday (Monday-Friday) nearest the given day. As an * example, if you were to specify "15W" as the value for the * day-of-month field, the meaning is: "the nearest weekday to the 15th of * the month". So if the 15th is a Saturday, the trigger will fire on * Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. * However if you specify "1W" as the value for day-of-month, and the * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not * 'jump' over the boundary of a month's days. The 'W' character can only be * specified when the day-of-month is a single day, not a range or list of days. *

* The 'L' and 'W' characters can also be combined for the day-of-month * expression to yield 'LW', which translates to "last weekday of the * month". *

* The '#' character is allowed for the day-of-week field. This character is * used to specify "the nth" XXX day of the month. For example, the * value of "6#3" in the day-of-week field means the third Friday of * the month (day 6 = Friday and "#3" = the 3rd one in the month). * Other examples: "2#1" = the first Monday of the month and * "4#5" = the fifth Wednesday of the month. Note that if you specify * "#5" and there is not 5 of the given day-of-week in the month, then * no firing will occur that month. If the '#' character is used, there can * only be one expression in the day-of-week field ("3#1,6#3" is * not valid, since there are two expressions). *

* *

* The legal characters and the names of months and days of the week are not * case sensitive. * *

* NOTES: *

    *
  • Support for specifying both a day-of-week and a day-of-month value is * not complete (you'll need to use the '?' character in one of these fields). *
  • *
  • Overflowing ranges is supported - that is, having a larger number on * the left hand side than the right. You might do 22-2 to catch 10 o'clock * at night until 2 o'clock in the morning, or you might have NOV-FEB. It is * very important to note that overuse of overflowing ranges creates ranges * that don't make sense and no effort has been made to determine which * interpretation CronExpression chooses. An example would be * "0 0 14-6 ? * FRI-MON".
  • *
*

* * @author Sharada Jambula, James House * @author Contributions from Mads Henderson * @author Refactoring from CronTrigger to CronExpression by Aaron Craven *

* Borrowed from quartz v2.3.1 */ public final class CronExpression implements Serializable, Cloneable { private static final long serialVersionUID = 12423409423L; protected static final int SECOND = 0; protected static final int MINUTE = 1; protected static final int HOUR = 2; protected static final int DAY_OF_MONTH = 3; protected static final int MONTH = 4; protected static final int DAY_OF_WEEK = 5; protected static final int YEAR = 6; protected static final int ALL_SPEC_INT = 99; // '*' protected static final int NO_SPEC_INT = 98; // '?' protected static final Integer ALL_SPEC = ALL_SPEC_INT; protected static final Integer NO_SPEC = NO_SPEC_INT; protected static final Map monthMap = new HashMap(20); protected static final Map dayMap = new HashMap(60); static { monthMap.put("JAN", 0); monthMap.put("FEB", 1); monthMap.put("MAR", 2); monthMap.put("APR", 3); monthMap.put("MAY", 4); monthMap.put("JUN", 5); monthMap.put("JUL", 6); monthMap.put("AUG", 7); monthMap.put("SEP", 8); monthMap.put("OCT", 9); monthMap.put("NOV", 10); monthMap.put("DEC", 11); dayMap.put("SUN", 1); dayMap.put("MON", 2); dayMap.put("TUE", 3); dayMap.put("WED", 4); dayMap.put("THU", 5); dayMap.put("FRI", 6); dayMap.put("SAT", 7); } private final String cronExpression; private TimeZone timeZone = null; protected transient TreeSet seconds; protected transient TreeSet minutes; protected transient TreeSet hours; protected transient TreeSet daysOfMonth; protected transient TreeSet months; protected transient TreeSet daysOfWeek; protected transient TreeSet years; protected transient boolean lastdayOfWeek = false; protected transient int nthdayOfWeek = 0; protected transient boolean lastdayOfMonth = false; protected transient boolean nearestWeekday = false; protected transient int lastdayOffset = 0; protected transient boolean expressionParsed = false; public static final int MAX_YEAR = Calendar.getInstance().get(Calendar.YEAR) + 100; /** * Constructs a new CronExpression based on the specified * parameter. * * @param cronExpression String representation of the cron expression the * new object should represent * @throws ParseException if the string expression cannot be parsed into a valid * CronExpression */ public CronExpression(String cronExpression) throws ParseException { if (cronExpression == null) { throw new IllegalArgumentException("cronExpression cannot be null"); } this.cronExpression = cronExpression.toUpperCase(Locale.US); buildExpression(this.cronExpression); } /** * Constructs a new {@code CronExpression} as a copy of an existing * instance. * * @param expression The existing cron expression to be copied */ public CronExpression(CronExpression expression) { /* * We don't call the other constructor here since we need to swallow the * ParseException. We also elide some of the sanity checking as it is * not logically trippable. */ this.cronExpression = expression.getCronExpression(); try { buildExpression(cronExpression); } catch (ParseException ex) { throw new AssertionError(); } if (expression.getTimeZone() != null) { setTimeZone((TimeZone) expression.getTimeZone().clone()); } } /** * Indicates whether the given date satisfies the cron expression. Note that * milliseconds are ignored, so two Dates falling on different milliseconds * of the same second will always have the same result here. * * @param date the date to evaluate * @return a boolean indicating whether the given date satisfies the cron * expression */ public boolean isSatisfiedBy(Date date) { Calendar testDateCal = Calendar.getInstance(getTimeZone()); testDateCal.setTime(date); testDateCal.set(Calendar.MILLISECOND, 0); Date originalDate = testDateCal.getTime(); testDateCal.add(Calendar.SECOND, -1); Date timeAfter = getTimeAfter(testDateCal.getTime()); return ((timeAfter != null) && (timeAfter.equals(originalDate))); } /** * Returns the next date/time after the given date/time which * satisfies the cron expression. * * @param date the date/time at which to begin the search for the next valid * date/time * @return the next valid date/time */ public Date getNextValidTimeAfter(Date date) { return getTimeAfter(date); } /** * Returns the next date/time after the given date/time which does * not satisfy the expression * * @param date the date/time at which to begin the search for the next * invalid date/time * @return the next valid date/time */ public Date getNextInvalidTimeAfter(Date date) { long difference = 1000; //move back to the nearest second so differences will be accurate Calendar adjustCal = Calendar.getInstance(getTimeZone()); adjustCal.setTime(date); adjustCal.set(Calendar.MILLISECOND, 0); Date lastDate = adjustCal.getTime(); Date newDate; //FUTURE_TODO: (QUARTZ-481) IMPROVE THIS! The following is a BAD solution to this problem. Performance will be very bad here, depending on the cron expression. It is, however A solution. //keep getting the next included time until it's farther than one second // apart. At that point, lastDate is the last valid fire time. We return // the second immediately following it. while (difference == 1000) { newDate = getTimeAfter(lastDate); if (newDate == null) break; difference = newDate.getTime() - lastDate.getTime(); if (difference == 1000) { lastDate = newDate; } } return new Date(lastDate.getTime() + 1000); } /** * Returns the time zone for which this CronExpression * will be resolved. */ public TimeZone getTimeZone() { if (timeZone == null) { timeZone = TimeZone.getDefault(); } return timeZone; } /** * Sets the time zone for which this CronExpression * will be resolved. */ public void setTimeZone(TimeZone timeZone) { this.timeZone = timeZone; } /** * Returns the string representation of the CronExpression * * @return a string representation of the CronExpression */ @Override public String toString() { return cronExpression; } /** * Indicates whether the specified cron expression can be parsed into a * valid cron expression * * @param cronExpression the expression to evaluate * @return a boolean indicating whether the given expression is a valid cron * expression */ public static boolean isValidExpression(String cronExpression) { try { new CronExpression(cronExpression); } catch (ParseException pe) { return false; } return true; } public static void validateExpression(String cronExpression) throws ParseException { new CronExpression(cronExpression); } //////////////////////////////////////////////////////////////////////////// // // Expression Parsing Functions // //////////////////////////////////////////////////////////////////////////// protected void buildExpression(String expression) throws ParseException { expressionParsed = true; try { if (seconds == null) { seconds = new TreeSet(); } if (minutes == null) { minutes = new TreeSet(); } if (hours == null) { hours = new TreeSet(); } if (daysOfMonth == null) { daysOfMonth = new TreeSet(); } if (months == null) { months = new TreeSet(); } if (daysOfWeek == null) { daysOfWeek = new TreeSet(); } if (years == null) { years = new TreeSet(); } int exprOn = SECOND; StringTokenizer exprsTok = new StringTokenizer(expression, " \t", false); while (exprsTok.hasMoreTokens() && exprOn <= YEAR) { String expr = exprsTok.nextToken().trim(); // throw an exception if L is used with other days of the month if (exprOn == DAY_OF_MONTH && expr.indexOf('L') != -1 && expr.length() > 1 && expr.contains(",")) { throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1); } // throw an exception if L is used with other days of the week if (exprOn == DAY_OF_WEEK && expr.indexOf('L') != -1 && expr.length() > 1 && expr.contains(",")) { throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1); } if (exprOn == DAY_OF_WEEK && expr.indexOf('#') != -1 && expr.indexOf('#', expr.indexOf('#') + 1) != -1) { throw new ParseException("Support for specifying multiple \"nth\" days is not implemented.", -1); } StringTokenizer vTok = new StringTokenizer(expr, ","); while (vTok.hasMoreTokens()) { String v = vTok.nextToken(); storeExpressionVals(0, v, exprOn); } exprOn++; } if (exprOn <= DAY_OF_WEEK) { throw new ParseException("Unexpected end of expression.", expression.length()); } if (exprOn <= YEAR) { storeExpressionVals(0, "*", YEAR); } TreeSet dow = getSet(DAY_OF_WEEK); TreeSet dom = getSet(DAY_OF_MONTH); // Copying the logic from the UnsupportedOperationException below boolean dayOfMSpec = !dom.contains(NO_SPEC); boolean dayOfWSpec = !dow.contains(NO_SPEC); if (!dayOfMSpec || dayOfWSpec) { if (!dayOfWSpec || dayOfMSpec) { throw new ParseException( "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0); } } } catch (ParseException pe) { throw pe; } catch (Exception e) { throw new ParseException("Illegal cron expression format (" + e.toString() + ")", 0); } } protected int storeExpressionVals(int pos, String s, int type) throws ParseException { int incr = 0; int i = skipWhiteSpace(pos, s); if (i >= s.length()) { return i; } char c = s.charAt(i); if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW")) && (!s.matches("^L-[0-9]*[W]?"))) { String sub = s.substring(i, i + 3); int sval = -1; int eval = -1; if (type == MONTH) { sval = getMonthNumber(sub) + 1; if (sval <= 0) { throw new ParseException("Invalid Month value: '" + sub + "'", i); } if (s.length() > i + 3) { c = s.charAt(i + 3); if (c == '-') { i += 4; sub = s.substring(i, i + 3); eval = getMonthNumber(sub) + 1; if (eval <= 0) { throw new ParseException("Invalid Month value: '" + sub + "'", i); } } } } else if (type == DAY_OF_WEEK) { sval = getDayOfWeekNumber(sub); if (sval < 0) { throw new ParseException("Invalid Day-of-Week value: '" + sub + "'", i); } if (s.length() > i + 3) { c = s.charAt(i + 3); if (c == '-') { i += 4; sub = s.substring(i, i + 3); eval = getDayOfWeekNumber(sub); if (eval < 0) { throw new ParseException( "Invalid Day-of-Week value: '" + sub + "'", i); } } else if (c == '#') { try { i += 4; nthdayOfWeek = Integer.parseInt(s.substring(i)); if (nthdayOfWeek < 1 || nthdayOfWeek > 5) { throw new Exception(); } } catch (Exception e) { throw new ParseException( "A numeric value between 1 and 5 must follow the '#' option", i); } } else if (c == 'L') { lastdayOfWeek = true; i++; } } } else { throw new ParseException( "Illegal characters for this position: '" + sub + "'", i); } if (eval != -1) { incr = 1; } addToSet(sval, eval, incr, type); return (i + 3); } if (c == '?') { i++; if ((i + 1) < s.length() && (s.charAt(i) != ' ' && s.charAt(i + 1) != '\t')) { throw new ParseException("Illegal character after '?': " + s.charAt(i), i); } if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) { throw new ParseException( "'?' can only be specified for Day-of-Month or Day-of-Week.", i); } if (type == DAY_OF_WEEK && !lastdayOfMonth) { int val = daysOfMonth.last(); if (val == NO_SPEC_INT) { throw new ParseException( "'?' can only be specified for Day-of-Month -OR- Day-of-Week.", i); } } addToSet(NO_SPEC_INT, -1, 0, type); return i; } if (c == '*' || c == '/') { if (c == '*' && (i + 1) >= s.length()) { addToSet(ALL_SPEC_INT, -1, incr, type); return i + 1; } else if (c == '/' && ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s .charAt(i + 1) == '\t')) { throw new ParseException("'/' must be followed by an integer.", i); } else if (c == '*') { i++; } c = s.charAt(i); if (c == '/') { // is an increment specified? i++; if (i >= s.length()) { throw new ParseException("Unexpected end of string.", i); } incr = getNumericValue(s, i); i++; if (incr > 10) { i++; } checkIncrementRange(incr, type, i); } else { incr = 1; } addToSet(ALL_SPEC_INT, -1, incr, type); return i; } else if (c == 'L') { i++; if (type == DAY_OF_MONTH) { lastdayOfMonth = true; } if (type == DAY_OF_WEEK) { addToSet(7, 7, 0, type); } if (type == DAY_OF_MONTH && s.length() > i) { c = s.charAt(i); if (c == '-') { ValueSet vs = getValue(0, s, i + 1); lastdayOffset = vs.value; if (lastdayOffset > 30) throw new ParseException("Offset from last day must be <= 30", i + 1); i = vs.pos; } if (s.length() > i) { c = s.charAt(i); if (c == 'W') { nearestWeekday = true; i++; } } } return i; } else if (c >= '0' && c <= '9') { int val = Integer.parseInt(String.valueOf(c)); i++; if (i >= s.length()) { addToSet(val, -1, -1, type); } else { c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(val, s, i); val = vs.value; i = vs.pos; } i = checkNext(i, s, val, type); return i; } } else { throw new ParseException("Unexpected character: " + c, i); } return i; } private void checkIncrementRange(int incr, int type, int idxPos) throws ParseException { if (incr > 59 && (type == SECOND || type == MINUTE)) { throw new ParseException("Increment > 60 : " + incr, idxPos); } else if (incr > 23 && (type == HOUR)) { throw new ParseException("Increment > 24 : " + incr, idxPos); } else if (incr > 31 && (type == DAY_OF_MONTH)) { throw new ParseException("Increment > 31 : " + incr, idxPos); } else if (incr > 7 && (type == DAY_OF_WEEK)) { throw new ParseException("Increment > 7 : " + incr, idxPos); } else if (incr > 12 && (type == MONTH)) { throw new ParseException("Increment > 12 : " + incr, idxPos); } } protected int checkNext(int pos, String s, int val, int type) throws ParseException { int end = -1; int i = pos; if (i >= s.length()) { addToSet(val, end, -1, type); return i; } char c = s.charAt(pos); if (c == 'L') { if (type == DAY_OF_WEEK) { if (val < 1 || val > 7) throw new ParseException("Day-of-Week values must be between 1 and 7", -1); lastdayOfWeek = true; } else { throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i); } TreeSet set = getSet(type); set.add(val); i++; return i; } if (c == 'W') { if (type == DAY_OF_MONTH) { nearestWeekday = true; } else { throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i); } if (val > 31) throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i); TreeSet set = getSet(type); set.add(val); i++; return i; } if (c == '#') { if (type != DAY_OF_WEEK) { throw new ParseException("'#' option is not valid here. (pos=" + i + ")", i); } i++; try { nthdayOfWeek = Integer.parseInt(s.substring(i)); if (nthdayOfWeek < 1 || nthdayOfWeek > 5) { throw new Exception(); } } catch (Exception e) { throw new ParseException( "A numeric value between 1 and 5 must follow the '#' option", i); } TreeSet set = getSet(type); set.add(val); i++; return i; } if (c == '-') { i++; c = s.charAt(i); int v = Integer.parseInt(String.valueOf(c)); end = v; i++; if (i >= s.length()) { addToSet(val, end, 1, type); return i; } c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(v, s, i); end = vs.value; i = vs.pos; } if (i < s.length() && ((c = s.charAt(i)) == '/')) { i++; c = s.charAt(i); int v2 = Integer.parseInt(String.valueOf(c)); i++; if (i >= s.length()) { addToSet(val, end, v2, type); return i; } c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(v2, s, i); int v3 = vs.value; addToSet(val, end, v3, type); i = vs.pos; return i; } else { addToSet(val, end, v2, type); return i; } } else { addToSet(val, end, 1, type); return i; } } if (c == '/') { if ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s.charAt(i + 1) == '\t') { throw new ParseException("'/' must be followed by an integer.", i); } i++; c = s.charAt(i); int v2 = Integer.parseInt(String.valueOf(c)); i++; if (i >= s.length()) { checkIncrementRange(v2, type, i); addToSet(val, end, v2, type); return i; } c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(v2, s, i); int v3 = vs.value; checkIncrementRange(v3, type, i); addToSet(val, end, v3, type); i = vs.pos; return i; } else { throw new ParseException("Unexpected character '" + c + "' after '/'", i); } } addToSet(val, end, 0, type); i++; return i; } public String getCronExpression() { return cronExpression; } public String getExpressionSummary() { StringBuilder buf = new StringBuilder(); buf.append("seconds: "); buf.append(getExpressionSetSummary(seconds)); buf.append("\n"); buf.append("minutes: "); buf.append(getExpressionSetSummary(minutes)); buf.append("\n"); buf.append("hours: "); buf.append(getExpressionSetSummary(hours)); buf.append("\n"); buf.append("daysOfMonth: "); buf.append(getExpressionSetSummary(daysOfMonth)); buf.append("\n"); buf.append("months: "); buf.append(getExpressionSetSummary(months)); buf.append("\n"); buf.append("daysOfWeek: "); buf.append(getExpressionSetSummary(daysOfWeek)); buf.append("\n"); buf.append("lastdayOfWeek: "); buf.append(lastdayOfWeek); buf.append("\n"); buf.append("nearestWeekday: "); buf.append(nearestWeekday); buf.append("\n"); buf.append("NthDayOfWeek: "); buf.append(nthdayOfWeek); buf.append("\n"); buf.append("lastdayOfMonth: "); buf.append(lastdayOfMonth); buf.append("\n"); buf.append("years: "); buf.append(getExpressionSetSummary(years)); buf.append("\n"); return buf.toString(); } protected String getExpressionSetSummary(java.util.Set set) { if (set.contains(NO_SPEC)) { return "?"; } if (set.contains(ALL_SPEC)) { return "*"; } StringBuilder buf = new StringBuilder(); Iterator itr = set.iterator(); boolean first = true; while (itr.hasNext()) { Integer iVal = itr.next(); String val = iVal.toString(); if (!first) { buf.append(","); } buf.append(val); first = false; } return buf.toString(); } protected String getExpressionSetSummary(java.util.ArrayList list) { if (list.contains(NO_SPEC)) { return "?"; } if (list.contains(ALL_SPEC)) { return "*"; } StringBuilder buf = new StringBuilder(); Iterator itr = list.iterator(); boolean first = true; while (itr.hasNext()) { Integer iVal = itr.next(); String val = iVal.toString(); if (!first) { buf.append(","); } buf.append(val); first = false; } return buf.toString(); } protected int skipWhiteSpace(int i, String s) { for (; i < s.length() && (s.charAt(i) == ' ' || s.charAt(i) == '\t'); i++) { } return i; } protected int findNextWhiteSpace(int i, String s) { for (; i < s.length() && (s.charAt(i) != ' ' || s.charAt(i) != '\t'); i++) { } return i; } protected void addToSet(int val, int end, int incr, int type) throws ParseException { TreeSet set = getSet(type); if (type == SECOND || type == MINUTE) { if ((val < 0 || val > 59 || end > 59) && (val != ALL_SPEC_INT)) { throw new ParseException( "Minute and Second values must be between 0 and 59", -1); } } else if (type == HOUR) { if ((val < 0 || val > 23 || end > 23) && (val != ALL_SPEC_INT)) { throw new ParseException( "Hour values must be between 0 and 23", -1); } } else if (type == DAY_OF_MONTH) { if ((val < 1 || val > 31 || end > 31) && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) { throw new ParseException( "Day of month values must be between 1 and 31", -1); } } else if (type == MONTH) { if ((val < 1 || val > 12 || end > 12) && (val != ALL_SPEC_INT)) { throw new ParseException( "Month values must be between 1 and 12", -1); } } else if (type == DAY_OF_WEEK) { if ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT) && (val != NO_SPEC_INT)) { throw new ParseException( "Day-of-Week values must be between 1 and 7", -1); } } if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) { if (val != -1) { set.add(val); } else { set.add(NO_SPEC); } return; } int startAt = val; int stopAt = end; if (val == ALL_SPEC_INT && incr <= 0) { incr = 1; set.add(ALL_SPEC); // put in a marker, but also fill values } if (type == SECOND || type == MINUTE) { if (stopAt == -1) { stopAt = 59; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 0; } } else if (type == HOUR) { if (stopAt == -1) { stopAt = 23; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 0; } } else if (type == DAY_OF_MONTH) { if (stopAt == -1) { stopAt = 31; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1; } } else if (type == MONTH) { if (stopAt == -1) { stopAt = 12; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1; } } else if (type == DAY_OF_WEEK) { if (stopAt == -1) { stopAt = 7; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1; } } else if (type == YEAR) { if (stopAt == -1) { stopAt = MAX_YEAR; } if (startAt == -1 || startAt == ALL_SPEC_INT) { startAt = 1970; } } // if the end of the range is before the start, then we need to overflow into // the next day, month etc. This is done by adding the maximum amount for that // type, and using modulus max to determine the value being added. int max = -1; if (stopAt < startAt) { switch (type) { case SECOND: max = 60; break; case MINUTE: max = 60; break; case HOUR: max = 24; break; case MONTH: max = 12; break; case DAY_OF_WEEK: max = 7; break; case DAY_OF_MONTH: max = 31; break; case YEAR: throw new IllegalArgumentException("Start year must be less than stop year"); default: throw new IllegalArgumentException("Unexpected type encountered"); } stopAt += max; } for (int i = startAt; i <= stopAt; i += incr) { if (max == -1) { // ie: there's no max to overflow over set.add(i); } else { // take the modulus to get the real value int i2 = i % max; // 1-indexed ranges should not include 0, and should include their max if (i2 == 0 && (type == MONTH || type == DAY_OF_WEEK || type == DAY_OF_MONTH)) { i2 = max; } set.add(i2); } } } TreeSet getSet(int type) { switch (type) { case SECOND: return seconds; case MINUTE: return minutes; case HOUR: return hours; case DAY_OF_MONTH: return daysOfMonth; case MONTH: return months; case DAY_OF_WEEK: return daysOfWeek; case YEAR: return years; default: return null; } } protected ValueSet getValue(int v, String s, int i) { char c = s.charAt(i); StringBuilder s1 = new StringBuilder(String.valueOf(v)); while (c >= '0' && c <= '9') { s1.append(c); i++; if (i >= s.length()) { break; } c = s.charAt(i); } ValueSet val = new ValueSet(); val.pos = (i < s.length()) ? i : i + 1; val.value = Integer.parseInt(s1.toString()); return val; } protected int getNumericValue(String s, int i) { int endOfVal = findNextWhiteSpace(i, s); String val = s.substring(i, endOfVal); return Integer.parseInt(val); } protected int getMonthNumber(String s) { Integer integer = monthMap.get(s); if (integer == null) { return -1; } return integer; } protected int getDayOfWeekNumber(String s) { Integer integer = dayMap.get(s); if (integer == null) { return -1; } return integer; } //////////////////////////////////////////////////////////////////////////// // // Computation Functions // //////////////////////////////////////////////////////////////////////////// public Date getTimeAfter(Date afterTime) { // Computation is based on Gregorian year only. Calendar cl = new java.util.GregorianCalendar(getTimeZone()); // move ahead one second, since we're computing the time *after* the // given time afterTime = new Date(afterTime.getTime() + 1000); // CronTrigger does not deal with milliseconds cl.setTime(afterTime); cl.set(Calendar.MILLISECOND, 0); boolean gotOne = false; // loop until we've computed the next time, or we've past the endTime while (!gotOne) { //if (endTime != null && cl.getTime().after(endTime)) return null; if (cl.get(Calendar.YEAR) > 2999) { // prevent endless loop... return null; } SortedSet st = null; int t = 0; int sec = cl.get(Calendar.SECOND); int min = cl.get(Calendar.MINUTE); // get second................................................. st = seconds.tailSet(sec); if (st != null && st.size() != 0) { sec = st.first(); } else { sec = seconds.first(); min++; cl.set(Calendar.MINUTE, min); } cl.set(Calendar.SECOND, sec); min = cl.get(Calendar.MINUTE); int hr = cl.get(Calendar.HOUR_OF_DAY); t = -1; // get minute................................................. st = minutes.tailSet(min); if (st != null && st.size() != 0) { t = min; min = st.first(); } else { min = minutes.first(); hr++; } if (min != t) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, min); setCalendarHour(cl, hr); continue; } cl.set(Calendar.MINUTE, min); hr = cl.get(Calendar.HOUR_OF_DAY); int day = cl.get(Calendar.DAY_OF_MONTH); t = -1; // get hour................................................... st = hours.tailSet(hr); if (st != null && st.size() != 0) { t = hr; hr = st.first(); } else { hr = hours.first(); day++; } if (hr != t) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.DAY_OF_MONTH, day); setCalendarHour(cl, hr); continue; } cl.set(Calendar.HOUR_OF_DAY, hr); day = cl.get(Calendar.DAY_OF_MONTH); int mon = cl.get(Calendar.MONTH) + 1; // '+ 1' because calendar is 0-based for this field, and we are // 1-based t = -1; int tmon = mon; // get day................................................... boolean dayOfMSpec = !daysOfMonth.contains(NO_SPEC); boolean dayOfWSpec = !daysOfWeek.contains(NO_SPEC); if (dayOfMSpec && !dayOfWSpec) { // get day by day of month rule st = daysOfMonth.tailSet(day); if (lastdayOfMonth) { if (!nearestWeekday) { t = day; day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); day -= lastdayOffset; if (t > day) { mon++; if (mon > 12) { mon = 1; tmon = 3333; // ensure test of mon != tmon further below fails cl.add(Calendar.YEAR, 1); } day = 1; } } else { t = day; day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); day -= lastdayOffset; Calendar tcal = Calendar.getInstance(getTimeZone()); tcal.set(Calendar.SECOND, 0); tcal.set(Calendar.MINUTE, 0); tcal.set(Calendar.HOUR_OF_DAY, 0); tcal.set(Calendar.DAY_OF_MONTH, day); tcal.set(Calendar.MONTH, mon - 1); tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR)); int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); int dow = tcal.get(Calendar.DAY_OF_WEEK); if (dow == Calendar.SATURDAY && day == 1) { day += 2; } else if (dow == Calendar.SATURDAY) { day -= 1; } else if (dow == Calendar.SUNDAY && day == ldom) { day -= 2; } else if (dow == Calendar.SUNDAY) { day += 1; } tcal.set(Calendar.SECOND, sec); tcal.set(Calendar.MINUTE, min); tcal.set(Calendar.HOUR_OF_DAY, hr); tcal.set(Calendar.DAY_OF_MONTH, day); tcal.set(Calendar.MONTH, mon - 1); Date nTime = tcal.getTime(); if (nTime.before(afterTime)) { day = 1; mon++; } } } else if (nearestWeekday) { t = day; day = daysOfMonth.first(); Calendar tcal = Calendar.getInstance(getTimeZone()); tcal.set(Calendar.SECOND, 0); tcal.set(Calendar.MINUTE, 0); tcal.set(Calendar.HOUR_OF_DAY, 0); tcal.set(Calendar.DAY_OF_MONTH, day); tcal.set(Calendar.MONTH, mon - 1); tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR)); int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); int dow = tcal.get(Calendar.DAY_OF_WEEK); if (dow == Calendar.SATURDAY && day == 1) { day += 2; } else if (dow == Calendar.SATURDAY) { day -= 1; } else if (dow == Calendar.SUNDAY && day == ldom) { day -= 2; } else if (dow == Calendar.SUNDAY) { day += 1; } tcal.set(Calendar.SECOND, sec); tcal.set(Calendar.MINUTE, min); tcal.set(Calendar.HOUR_OF_DAY, hr); tcal.set(Calendar.DAY_OF_MONTH, day); tcal.set(Calendar.MONTH, mon - 1); Date nTime = tcal.getTime(); if (nTime.before(afterTime)) { day = daysOfMonth.first(); mon++; } } else if (st != null && st.size() != 0) { t = day; day = st.first(); // make sure we don't over-run a short month, such as february int lastDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); if (day > lastDay) { day = daysOfMonth.first(); mon++; } } else { day = daysOfMonth.first(); mon++; } if (day != t || mon != tmon) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, day); cl.set(Calendar.MONTH, mon - 1); // '- 1' because calendar is 0-based for this field, and we // are 1-based continue; } } else if (dayOfWSpec && !dayOfMSpec) { // get day by day of week rule if (lastdayOfWeek) { // are we looking for the last XXX day of // the month? int dow = daysOfWeek.first(); // desired // d-o-w int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w int daysToAdd = 0; if (cDow < dow) { daysToAdd = dow - cDow; } if (cDow > dow) { daysToAdd = dow + (7 - cDow); } int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); if (day + daysToAdd > lDay) { // did we already miss the // last one? cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, 1); cl.set(Calendar.MONTH, mon); // no '- 1' here because we are promoting the month continue; } // find date of last occurrence of this day in this month... while ((day + daysToAdd + 7) <= lDay) { daysToAdd += 7; } day += daysToAdd; if (daysToAdd > 0) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, day); cl.set(Calendar.MONTH, mon - 1); // '- 1' here because we are not promoting the month continue; } } else if (nthdayOfWeek != 0) { // are we looking for the Nth XXX day in the month? int dow = daysOfWeek.first(); // desired // d-o-w int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w int daysToAdd = 0; if (cDow < dow) { daysToAdd = dow - cDow; } else if (cDow > dow) { daysToAdd = dow + (7 - cDow); } boolean dayShifted = false; if (daysToAdd > 0) { dayShifted = true; } day += daysToAdd; int weekOfMonth = day / 7; if (day % 7 > 0) { weekOfMonth++; } daysToAdd = (nthdayOfWeek - weekOfMonth) * 7; day += daysToAdd; if (daysToAdd < 0 || day > getLastDayOfMonth(mon, cl .get(Calendar.YEAR))) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, 1); cl.set(Calendar.MONTH, mon); // no '- 1' here because we are promoting the month continue; } else if (daysToAdd > 0 || dayShifted) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, day); cl.set(Calendar.MONTH, mon - 1); // '- 1' here because we are NOT promoting the month continue; } } else { int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w int dow = daysOfWeek.first(); // desired // d-o-w st = daysOfWeek.tailSet(cDow); if (st != null && st.size() > 0) { dow = st.first(); } int daysToAdd = 0; if (cDow < dow) { daysToAdd = dow - cDow; } if (cDow > dow) { daysToAdd = dow + (7 - cDow); } int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); if (day + daysToAdd > lDay) { // will we pass the end of // the month? cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, 1); cl.set(Calendar.MONTH, mon); // no '- 1' here because we are promoting the month continue; } else if (daysToAdd > 0) { // are we swithing days? cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, day + daysToAdd); cl.set(Calendar.MONTH, mon - 1); // '- 1' because calendar is 0-based for this field, // and we are 1-based continue; } } } else { // dayOfWSpec && !dayOfMSpec throw new UnsupportedOperationException( "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented."); } cl.set(Calendar.DAY_OF_MONTH, day); mon = cl.get(Calendar.MONTH) + 1; // '+ 1' because calendar is 0-based for this field, and we are // 1-based int year = cl.get(Calendar.YEAR); t = -1; // test for expressions that never generate a valid fire date, // but keep looping... if (year > MAX_YEAR) { return null; } // get month................................................... st = months.tailSet(mon); if (st != null && st.size() != 0) { t = mon; mon = st.first(); } else { mon = months.first(); year++; } if (mon != t) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, 1); cl.set(Calendar.MONTH, mon - 1); // '- 1' because calendar is 0-based for this field, and we are // 1-based cl.set(Calendar.YEAR, year); continue; } cl.set(Calendar.MONTH, mon - 1); // '- 1' because calendar is 0-based for this field, and we are // 1-based year = cl.get(Calendar.YEAR); t = -1; // get year................................................... st = years.tailSet(year); if (st != null && st.size() != 0) { t = year; year = st.first(); } else { return null; // ran out of years... } if (year != t) { cl.set(Calendar.SECOND, 0); cl.set(Calendar.MINUTE, 0); cl.set(Calendar.HOUR_OF_DAY, 0); cl.set(Calendar.DAY_OF_MONTH, 1); cl.set(Calendar.MONTH, 0); // '- 1' because calendar is 0-based for this field, and we are // 1-based cl.set(Calendar.YEAR, year); continue; } cl.set(Calendar.YEAR, year); gotOne = true; } // while( !done ) return cl.getTime(); } /** * Advance the calendar to the particular hour paying particular attention * to daylight saving problems. * * @param cal the calendar to operate on * @param hour the hour to set */ protected void setCalendarHour(Calendar cal, int hour) { cal.set(Calendar.HOUR_OF_DAY, hour); if (cal.get(Calendar.HOUR_OF_DAY) != hour && hour != 24) { cal.set(Calendar.HOUR_OF_DAY, hour + 1); } } /** * NOT YET IMPLEMENTED: Returns the time before the given time * that the CronExpression matches. */ public Date getTimeBefore(Date endTime) { // FUTURE_TODO: implement QUARTZ-423 return null; } /** * NOT YET IMPLEMENTED: Returns the final time that the * CronExpression will match. */ public Date getFinalFireTime() { // FUTURE_TODO: implement QUARTZ-423 return null; } protected boolean isLeapYear(int year) { return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)); } protected int getLastDayOfMonth(int monthNum, int year) { switch (monthNum) { case 1: return 31; case 2: return (isLeapYear(year)) ? 29 : 28; case 3: return 31; case 4: return 30; case 5: return 31; case 6: return 30; case 7: return 31; case 8: return 31; case 9: return 30; case 10: return 31; case 11: return 30; case 12: return 31; default: throw new IllegalArgumentException("Illegal month number: " + monthNum); } } private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException { stream.defaultReadObject(); try { buildExpression(cronExpression); } catch (Exception ignore) { } // never happens } @Override @Deprecated public Object clone() { return new CronExpression(this); } } class ValueSet { public int value; public int pos; } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/exception/XxlJobException.java ================================================ package com.xxl.job.admin.core.exception; /** * @author xuxueli 2019-05-04 23:19:29 */ public class XxlJobException extends RuntimeException { public XxlJobException() { } public XxlJobException(String message) { super(message); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobGroup.java ================================================ package com.xxl.job.admin.core.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; /** * Created by xuxueli on 16/9/30. */ public class XxlJobGroup { private int id; private String appname; private String title; private int addressType; // 执行器地址类型:0=自动注册、1=手动录入 private String addressList; // 执行器地址列表,多地址逗号分隔(手动录入) private Date updateTime; // registry list private List registryList; // 执行器地址列表(系统注册) public List getRegistryList() { if (addressList != null && addressList.trim().length() > 0) { registryList = new ArrayList(Arrays.asList(addressList.split(","))); } return registryList; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAppname() { return appname; } public void setAppname(String appname) { this.appname = appname; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getAddressType() { return addressType; } public void setAddressType(int addressType) { this.addressType = addressType; } public String getAddressList() { return addressList; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public void setAddressList(String addressList) { this.addressList = addressList; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java ================================================ package com.xxl.job.admin.core.model; import java.util.Date; /** * xxl-job info * * @author xuxueli 2016-1-12 18:25:49 */ public class XxlJobInfo { private int id; // 主键ID private int jobGroup; // 执行器主键ID private String jobDesc; private Date addTime; private Date updateTime; private String author; // 负责人 private String alarmEmail; // 报警邮件 private String scheduleType; // 调度类型 private String scheduleConf; // 调度配置,值含义取决于调度类型 private String misfireStrategy; // 调度过期策略 private String executorRouteStrategy; // 执行器路由策略 private String executorHandler; // 执行器,任务Handler名称 private String executorParam; // 执行器,任务参数 private String executorBlockStrategy; // 阻塞处理策略 private int executorTimeout; // 任务执行超时时间,单位秒 private int executorFailRetryCount; // 失败重试次数 private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum private String glueSource; // GLUE源代码 private String glueRemark; // GLUE备注 private Date glueUpdatetime; // GLUE更新时间 private String childJobId; // 子任务ID,多个逗号分隔 private int triggerStatus; // 调度状态:0-停止,1-运行 private long triggerLastTime; // 上次调度时间 private long triggerNextTime; // 下次调度时间 public int getId() { return id; } public void setId(int id) { this.id = id; } public int getJobGroup() { return jobGroup; } public void setJobGroup(int jobGroup) { this.jobGroup = jobGroup; } public String getJobDesc() { return jobDesc; } public void setJobDesc(String jobDesc) { this.jobDesc = jobDesc; } public Date getAddTime() { return addTime; } public void setAddTime(Date addTime) { this.addTime = addTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getAlarmEmail() { return alarmEmail; } public void setAlarmEmail(String alarmEmail) { this.alarmEmail = alarmEmail; } public String getScheduleType() { return scheduleType; } public void setScheduleType(String scheduleType) { this.scheduleType = scheduleType; } public String getScheduleConf() { return scheduleConf; } public void setScheduleConf(String scheduleConf) { this.scheduleConf = scheduleConf; } public String getMisfireStrategy() { return misfireStrategy; } public void setMisfireStrategy(String misfireStrategy) { this.misfireStrategy = misfireStrategy; } public String getExecutorRouteStrategy() { return executorRouteStrategy; } public void setExecutorRouteStrategy(String executorRouteStrategy) { this.executorRouteStrategy = executorRouteStrategy; } public String getExecutorHandler() { return executorHandler; } public void setExecutorHandler(String executorHandler) { this.executorHandler = executorHandler; } public String getExecutorParam() { return executorParam; } public void setExecutorParam(String executorParam) { this.executorParam = executorParam; } public String getExecutorBlockStrategy() { return executorBlockStrategy; } public void setExecutorBlockStrategy(String executorBlockStrategy) { this.executorBlockStrategy = executorBlockStrategy; } public int getExecutorTimeout() { return executorTimeout; } public void setExecutorTimeout(int executorTimeout) { this.executorTimeout = executorTimeout; } public int getExecutorFailRetryCount() { return executorFailRetryCount; } public void setExecutorFailRetryCount(int executorFailRetryCount) { this.executorFailRetryCount = executorFailRetryCount; } public String getGlueType() { return glueType; } public void setGlueType(String glueType) { this.glueType = glueType; } public String getGlueSource() { return glueSource; } public void setGlueSource(String glueSource) { this.glueSource = glueSource; } public String getGlueRemark() { return glueRemark; } public void setGlueRemark(String glueRemark) { this.glueRemark = glueRemark; } public Date getGlueUpdatetime() { return glueUpdatetime; } public void setGlueUpdatetime(Date glueUpdatetime) { this.glueUpdatetime = glueUpdatetime; } public String getChildJobId() { return childJobId; } public void setChildJobId(String childJobId) { this.childJobId = childJobId; } public int getTriggerStatus() { return triggerStatus; } public void setTriggerStatus(int triggerStatus) { this.triggerStatus = triggerStatus; } public long getTriggerLastTime() { return triggerLastTime; } public void setTriggerLastTime(long triggerLastTime) { this.triggerLastTime = triggerLastTime; } public long getTriggerNextTime() { return triggerNextTime; } public void setTriggerNextTime(long triggerNextTime) { this.triggerNextTime = triggerNextTime; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobLog.java ================================================ package com.xxl.job.admin.core.model; import java.util.Date; /** * xxl-job log, used to track trigger process * * @author xuxueli 2015-12-19 23:19:09 */ public class XxlJobLog { private long id; // job info private int jobGroup; private int jobId; // execute info private String executorAddress; private String executorHandler; private String executorParam; private String executorShardingParam; private int executorFailRetryCount; // trigger info private Date triggerTime; private int triggerCode; private String triggerMsg; // handle info private Date handleTime; private int handleCode; private String handleMsg; // alarm info private int alarmStatus; public long getId() { return id; } public void setId(long id) { this.id = id; } public int getJobGroup() { return jobGroup; } public void setJobGroup(int jobGroup) { this.jobGroup = jobGroup; } public int getJobId() { return jobId; } public void setJobId(int jobId) { this.jobId = jobId; } public String getExecutorAddress() { return executorAddress; } public void setExecutorAddress(String executorAddress) { this.executorAddress = executorAddress; } public String getExecutorHandler() { return executorHandler; } public void setExecutorHandler(String executorHandler) { this.executorHandler = executorHandler; } public String getExecutorParam() { return executorParam; } public void setExecutorParam(String executorParam) { this.executorParam = executorParam; } public String getExecutorShardingParam() { return executorShardingParam; } public void setExecutorShardingParam(String executorShardingParam) { this.executorShardingParam = executorShardingParam; } public int getExecutorFailRetryCount() { return executorFailRetryCount; } public void setExecutorFailRetryCount(int executorFailRetryCount) { this.executorFailRetryCount = executorFailRetryCount; } public Date getTriggerTime() { return triggerTime; } public void setTriggerTime(Date triggerTime) { this.triggerTime = triggerTime; } public int getTriggerCode() { return triggerCode; } public void setTriggerCode(int triggerCode) { this.triggerCode = triggerCode; } public String getTriggerMsg() { return triggerMsg; } public void setTriggerMsg(String triggerMsg) { this.triggerMsg = triggerMsg; } public Date getHandleTime() { return handleTime; } public void setHandleTime(Date handleTime) { this.handleTime = handleTime; } public int getHandleCode() { return handleCode; } public void setHandleCode(int handleCode) { this.handleCode = handleCode; } public String getHandleMsg() { return handleMsg; } public void setHandleMsg(String handleMsg) { this.handleMsg = handleMsg; } public int getAlarmStatus() { return alarmStatus; } public void setAlarmStatus(int alarmStatus) { this.alarmStatus = alarmStatus; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobLogGlue.java ================================================ package com.xxl.job.admin.core.model; import java.util.Date; /** * xxl-job log for glue, used to track job code process * * @author xuxueli 2016-5-19 17:57:46 */ public class XxlJobLogGlue { private int id; private int jobId; // 任务主键ID private String glueType; // GLUE类型 #com.xxl.job.core.glue.GlueTypeEnum private String glueSource; private String glueRemark; private Date addTime; private Date updateTime; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getJobId() { return jobId; } public void setJobId(int jobId) { this.jobId = jobId; } public String getGlueType() { return glueType; } public void setGlueType(String glueType) { this.glueType = glueType; } public String getGlueSource() { return glueSource; } public void setGlueSource(String glueSource) { this.glueSource = glueSource; } public String getGlueRemark() { return glueRemark; } public void setGlueRemark(String glueRemark) { this.glueRemark = glueRemark; } public Date getAddTime() { return addTime; } public void setAddTime(Date addTime) { this.addTime = addTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobLogReport.java ================================================ package com.xxl.job.admin.core.model; import java.util.Date; public class XxlJobLogReport { private int id; private Date triggerDay; private int runningCount; private int sucCount; private int failCount; public int getId() { return id; } public void setId(int id) { this.id = id; } public Date getTriggerDay() { return triggerDay; } public void setTriggerDay(Date triggerDay) { this.triggerDay = triggerDay; } public int getRunningCount() { return runningCount; } public void setRunningCount(int runningCount) { this.runningCount = runningCount; } public int getSucCount() { return sucCount; } public void setSucCount(int sucCount) { this.sucCount = sucCount; } public int getFailCount() { return failCount; } public void setFailCount(int failCount) { this.failCount = failCount; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobRegistry.java ================================================ package com.xxl.job.admin.core.model; import java.util.Date; /** * Created by xuxueli on 16/9/30. */ public class XxlJobRegistry { private int id; private String registryGroup; private String registryKey; private String registryValue; private Date updateTime; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getRegistryGroup() { return registryGroup; } public void setRegistryGroup(String registryGroup) { this.registryGroup = registryGroup; } public String getRegistryKey() { return registryKey; } public void setRegistryKey(String registryKey) { this.registryKey = registryKey; } public String getRegistryValue() { return registryValue; } public void setRegistryValue(String registryValue) { this.registryValue = registryValue; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobUser.java ================================================ package com.xxl.job.admin.core.model; import org.springframework.util.StringUtils; /** * @author xuxueli 2019-05-04 16:43:12 */ public class XxlJobUser { private int id; private String username; // 账号 private String password; // 密码 private int role; // 角色:0-普通用户、1-管理员 private String permission; // 权限:执行器ID列表,多个逗号分割 public int getId() { return id; } public void setId(int id) { this.id = id; } 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 int getRole() { return role; } public void setRole(int role) { this.role = role; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } // plugin public boolean validPermission(int jobGroup) { if (this.role == 1) { return true; } else { if (StringUtils.hasText(this.permission)) { for (String permissionItem : this.permission.split(",")) { if (String.valueOf(jobGroup).equals(permissionItem)) { return true; } } } return false; } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/old/RemoteHttpJobBean.java ================================================ package com.xxl.job.admin.core.old;//package com.xxl.job.admin.core.jobbean; // //import com.xxl.job.admin.core.thread.JobTriggerPoolHelper; //import com.xxl.job.admin.core.trigger.TriggerTypeEnum; //import org.quartz.JobExecutionContext; //import org.quartz.JobExecutionException; //import org.quartz.JobKey; //import org.slf4j.Logger; //import org.slf4j.LoggerFactory; //import org.springframework.scheduling.quartz.QuartzJobBean; // ///** // * http job bean // * “@DisallowConcurrentExecution” disable concurrent, thread size can not be only one, better given more // * @author xuxueli 2015-12-17 18:20:34 // */ ////@DisallowConcurrentExecution //public class RemoteHttpJobBean extends QuartzJobBean { // private static Logger logger = LoggerFactory.getLogger(RemoteHttpJobBean.class); // // @Override // protected void executeInternal(JobExecutionContext context) // throws JobExecutionException { // // // load jobId // JobKey jobKey = context.getTrigger().getJobKey(); // Integer jobId = Integer.valueOf(jobKey.getName()); // // // } // //} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/old/XxlJobDynamicScheduler.java ================================================ package com.xxl.job.admin.core.old;//package com.xxl.job.admin.core.schedule; // //import com.xxl.job.admin.core.conf.XxlJobAdminConfig; //import com.xxl.job.admin.core.jobbean.RemoteHttpJobBean; //import com.xxl.job.admin.core.model.XxlJobInfo; //import com.xxl.job.admin.core.thread.JobFailMonitorHelper; //import com.xxl.job.admin.core.thread.JobRegistryMonitorHelper; //import com.xxl.job.admin.core.thread.JobTriggerPoolHelper; //import com.xxl.job.admin.core.util.I18nUtil; //import com.xxl.job.core.biz.AdminBiz; //import com.xxl.job.core.biz.ExecutorBiz; //import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; //import com.xxl.rpc.remoting.invoker.XxlRpcInvokerFactory; //import com.xxl.rpc.remoting.invoker.call.CallType; //import com.xxl.rpc.remoting.invoker.reference.XxlRpcReferenceBean; //import com.xxl.rpc.remoting.invoker.route.LoadBalance; //import com.xxl.rpc.remoting.net.NetEnum; //import com.xxl.rpc.remoting.net.impl.servlet.server.ServletServerHandler; //import com.xxl.rpc.remoting.provider.XxlRpcProviderFactory; //import com.xxl.rpc.serialize.Serializer; //import org.quartz.*; //import org.quartz.Trigger.TriggerState; //import org.quartz.impl.triggers.CronTriggerImpl; //import org.slf4j.Logger; //import org.slf4j.LoggerFactory; //import org.springframework.util.Assert; // //import javax.servlet.ServletException; //import javax.servlet.http.HttpServletRequest; //import javax.servlet.http.HttpServletResponse; //import java.io.IOException; //import java.util.Date; //import java.util.concurrent.ConcurrentHashMap; // ///** // * base quartz scheduler util // * @author xuxueli 2015-12-19 16:13:53 // */ //public final class XxlJobDynamicScheduler { // private static final Logger logger = LoggerFactory.getLogger(XxlJobDynamicScheduler_old.class); // // // ---------------------- param ---------------------- // // // scheduler // private static Scheduler scheduler; // public void setScheduler(Scheduler scheduler) { // XxlJobDynamicScheduler_old.scheduler = scheduler; // } // // // // ---------------------- init + destroy ---------------------- // public void start() throws Exception { // // valid // Assert.notNull(scheduler, "quartz scheduler is null"); // // // init i18n // initI18n(); // // // admin registry monitor run // JobRegistryMonitorHelper.getInstance().start(); // // // admin monitor run // JobFailMonitorHelper.getInstance().start(); // // // admin-server // initRpcProvider(); // // logger.info(">>>>>>>>> init xxl-job admin success."); // } // // // public void destroy() throws Exception { // // admin trigger pool stop // JobTriggerPoolHelper.toStop(); // // // admin registry stop // JobRegistryMonitorHelper.getInstance().toStop(); // // // admin monitor stop // JobFailMonitorHelper.getInstance().toStop(); // // // admin-server // stopRpcProvider(); // } // // // // ---------------------- I18n ---------------------- // // private void initI18n(){ // for (ExecutorBlockStrategyEnum item:ExecutorBlockStrategyEnum.values()) { // item.setTitle(I18nUtil.getString("jobconf_block_".concat(item.name()))); // } // } // // // // ---------------------- admin rpc provider (no server version) ---------------------- // private static ServletServerHandler servletServerHandler; // private void initRpcProvider(){ // // init // XxlRpcProviderFactory xxlRpcProviderFactory = new XxlRpcProviderFactory(); // xxlRpcProviderFactory.initConfig( // NetEnum.NETTY_HTTP, // Serializer.SerializeEnum.HESSIAN.getSerializer(), // null, // 0, // XxlJobAdminConfig.getAdminConfig().getAccessToken(), // null, // null); // // // add services // xxlRpcProviderFactory.addService(AdminBiz.class.getName(), null, XxlJobAdminConfig.getAdminConfig().getAdminBiz()); // // // servlet handler // servletServerHandler = new ServletServerHandler(xxlRpcProviderFactory); // } // private void stopRpcProvider() throws Exception { // XxlRpcInvokerFactory.getInstance().stop(); // } // public static void invokeAdminService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // servletServerHandler.handle(null, request, response); // } // // // // ---------------------- executor-client ---------------------- // private static ConcurrentHashMap executorBizRepository = new ConcurrentHashMap(); // public static ExecutorBiz getExecutorBiz(String address) throws Exception { // // valid // if (address==null || address.trim().length()==0) { // return null; // } // // // load-cache // address = address.trim(); // ExecutorBiz executorBiz = executorBizRepository.get(address); // if (executorBiz != null) { // return executorBiz; // } // // // set-cache // executorBiz = (ExecutorBiz) new XxlRpcReferenceBean( // NetEnum.NETTY_HTTP, // Serializer.SerializeEnum.HESSIAN.getSerializer(), // CallType.SYNC, // LoadBalance.ROUND, // ExecutorBiz.class, // null, // 5000, // address, // XxlJobAdminConfig.getAdminConfig().getAccessToken(), // null, // null).getObject(); // // executorBizRepository.put(address, executorBiz); // return executorBiz; // } // // // // ---------------------- schedule util ---------------------- // // /** // * fill job info // * // * @param jobInfo // */ // public static void fillJobInfo(XxlJobInfo jobInfo) { // // String name = String.valueOf(jobInfo.getId()); // // // trigger key // TriggerKey triggerKey = TriggerKey.triggerKey(name); // try { // // // trigger cron // Trigger trigger = scheduler.getTrigger(triggerKey); // if (trigger!=null && trigger instanceof CronTriggerImpl) { // String cronExpression = ((CronTriggerImpl) trigger).getCronExpression(); // jobInfo.setJobCron(cronExpression); // } // // // trigger state // TriggerState triggerState = scheduler.getTriggerState(triggerKey); // if (triggerState!=null) { // jobInfo.setJobStatus(triggerState.name()); // } // // //JobKey jobKey = new JobKey(jobInfo.getJobName(), String.valueOf(jobInfo.getJobGroup())); // //JobDetail jobDetail = scheduler.getJobDetail(jobKey); // //String jobClass = jobDetail.getJobClass().getName(); // // } catch (SchedulerException e) { // logger.error(e.getMessage(), e); // } // } // // // /** // * add trigger + job // * // * @param jobName // * @param cronExpression // * @return // * @throws SchedulerException // */ // public static boolean addJob(String jobName, String cronExpression) throws SchedulerException { // // 1、job key // TriggerKey triggerKey = TriggerKey.triggerKey(jobName); // JobKey jobKey = new JobKey(jobName); // // // 2、valid // if (scheduler.checkExists(triggerKey)) { // return true; // PASS // } // // // 3、corn trigger // CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing(); // withMisfireHandlingInstructionDoNothing 忽略掉调度终止过程中忽略的调度 // CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build(); // // // 4、job detail // Class jobClass_ = RemoteHttpJobBean.class; // Class.forName(jobInfo.getJobClass()); // JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build(); // // /*if (jobInfo.getJobData()!=null) { // JobDataMap jobDataMap = jobDetail.getJobDataMap(); // jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class)); // // JobExecutionContext context.getMergedJobDataMap().get("mailGuid"); // }*/ // // // 5、schedule job // Date date = scheduler.scheduleJob(jobDetail, cronTrigger); // // logger.info(">>>>>>>>>>> addJob success(quartz), jobDetail:{}, cronTrigger:{}, date:{}", jobDetail, cronTrigger, date); // return true; // } // // // /** // * remove trigger + job // * // * @param jobName // * @return // * @throws SchedulerException // */ // public static boolean removeJob(String jobName) throws SchedulerException { // // JobKey jobKey = new JobKey(jobName); // scheduler.deleteJob(jobKey); // // /*TriggerKey triggerKey = TriggerKey.triggerKey(jobName); // if (scheduler.checkExists(triggerKey)) { // scheduler.unscheduleJob(triggerKey); // trigger + job // }*/ // // logger.info(">>>>>>>>>>> removeJob success(quartz), jobKey:{}", jobKey); // return true; // } // // // /** // * updateJobCron // * // * @param jobName // * @param cronExpression // * @return // * @throws SchedulerException // */ // public static boolean updateJobCron(String jobName, String cronExpression) throws SchedulerException { // // // 1、job key // TriggerKey triggerKey = TriggerKey.triggerKey(jobName); // // // 2、valid // if (!scheduler.checkExists(triggerKey)) { // return true; // PASS // } // // CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); // // // 3、avoid repeat cron // String oldCron = oldTrigger.getCronExpression(); // if (oldCron.equals(cronExpression)){ // return true; // PASS // } // // // 4、new cron trigger // CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing(); // oldTrigger = oldTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build(); // // // 5、rescheduleJob // scheduler.rescheduleJob(triggerKey, oldTrigger); // // /* // JobKey jobKey = new JobKey(jobName); // // // old job detail // JobDetail jobDetail = scheduler.getJobDetail(jobKey); // // // new trigger // HashSet triggerSet = new HashSet(); // triggerSet.add(cronTrigger); // // cover trigger of job detail // scheduler.scheduleJob(jobDetail, triggerSet, true);*/ // // logger.info(">>>>>>>>>>> resumeJob success, JobName:{}", jobName); // return true; // } // // // /** // * pause // * // * @param jobName // * @return // * @throws SchedulerException // */ // /*public static boolean pauseJob(String jobName) throws SchedulerException { // // TriggerKey triggerKey = TriggerKey.triggerKey(jobName); // // boolean result = false; // if (scheduler.checkExists(triggerKey)) { // scheduler.pauseTrigger(triggerKey); // result = true; // } // // logger.info(">>>>>>>>>>> pauseJob {}, triggerKey:{}", (result?"success":"fail"),triggerKey); // return result; // }*/ // // // /** // * resume // * // * @param jobName // * @return // * @throws SchedulerException // */ // /*public static boolean resumeJob(String jobName) throws SchedulerException { // // TriggerKey triggerKey = TriggerKey.triggerKey(jobName); // // boolean result = false; // if (scheduler.checkExists(triggerKey)) { // scheduler.resumeTrigger(triggerKey); // result = true; // } // // logger.info(">>>>>>>>>>> resumeJob {}, triggerKey:{}", (result?"success":"fail"), triggerKey); // return result; // }*/ // // // /** // * run // * // * @param jobName // * @return // * @throws SchedulerException // */ // /*public static boolean triggerJob(String jobName) throws SchedulerException { // // TriggerKey : name + group // JobKey jobKey = new JobKey(jobName); // TriggerKey triggerKey = TriggerKey.triggerKey(jobName); // // boolean result = false; // if (scheduler.checkExists(triggerKey)) { // scheduler.triggerJob(jobKey); // result = true; // logger.info(">>>>>>>>>>> runJob success, jobKey:{}", jobKey); // } else { // logger.info(">>>>>>>>>>> runJob fail, jobKey:{}", jobKey); // } // return result; // }*/ // // // /** // * finaAllJobList // * // * @return // *//* // @Deprecated // public static List> finaAllJobList(){ // List> jobList = new ArrayList>(); // // try { // if (scheduler.getJobGroupNames()==null || scheduler.getJobGroupNames().size()==0) { // return null; // } // String groupName = scheduler.getJobGroupNames().get(0); // Set jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName)); // if (jobKeys!=null && jobKeys.size()>0) { // for (JobKey jobKey : jobKeys) { // TriggerKey triggerKey = TriggerKey.triggerKey(jobKey.getName(), Scheduler.DEFAULT_GROUP); // Trigger trigger = scheduler.getTrigger(triggerKey); // JobDetail jobDetail = scheduler.getJobDetail(jobKey); // TriggerState triggerState = scheduler.getTriggerState(triggerKey); // Map jobMap = new HashMap(); // jobMap.put("TriggerKey", triggerKey); // jobMap.put("Trigger", trigger); // jobMap.put("JobDetail", jobDetail); // jobMap.put("TriggerState", triggerState); // jobList.add(jobMap); // } // } // // } catch (SchedulerException e) { // logger.error(e.getMessage(), e); // return null; // } // return jobList; // }*/ // //} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/old/XxlJobThreadPool.java ================================================ package com.xxl.job.admin.core.old;//package com.xxl.job.admin.core.quartz; // //import org.quartz.SchedulerConfigException; //import org.quartz.spi.ThreadPool; // ///** // * single thread pool, for async trigger // * // * @author xuxueli 2019-03-06 // */ //public class XxlJobThreadPool implements ThreadPool { // // @Override // public boolean runInThread(Runnable runnable) { // // // async run // runnable.run(); // return true; // // //return false; // } // // @Override // public int blockForAvailableThreads() { // return 1; // } // // @Override // public void initialize() throws SchedulerConfigException { // // } // // @Override // public void shutdown(boolean waitForJobsToComplete) { // // } // // @Override // public int getPoolSize() { // return 1; // } // // @Override // public void setInstanceId(String schedInstId) { // // } // // @Override // public void setInstanceName(String schedName) { // // } // // // support // public void setThreadCount(int count) { // // // } // //} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java ================================================ package com.xxl.job.admin.core.route; import com.xxl.job.admin.core.route.strategy.*; import com.xxl.job.admin.core.util.I18nUtil; /** * Created by xuxueli on 17/3/10. */ public enum ExecutorRouteStrategyEnum { FIRST(I18nUtil.getString("jobconf_route_first"), new ExecutorRouteFirst()), LAST(I18nUtil.getString("jobconf_route_last"), new ExecutorRouteLast()), ROUND(I18nUtil.getString("jobconf_route_round"), new ExecutorRouteRound()), RANDOM(I18nUtil.getString("jobconf_route_random"), new ExecutorRouteRandom()), CONSISTENT_HASH(I18nUtil.getString("jobconf_route_consistenthash"), new ExecutorRouteConsistentHash()), LEAST_FREQUENTLY_USED(I18nUtil.getString("jobconf_route_lfu"), new ExecutorRouteLFU()), LEAST_RECENTLY_USED(I18nUtil.getString("jobconf_route_lru"), new ExecutorRouteLRU()), FAILOVER(I18nUtil.getString("jobconf_route_failover"), new ExecutorRouteFailover()), BUSYOVER(I18nUtil.getString("jobconf_route_busyover"), new ExecutorRouteBusyover()), SHARDING_BROADCAST(I18nUtil.getString("jobconf_route_shard"), null); ExecutorRouteStrategyEnum(String title, ExecutorRouter router) { this.title = title; this.router = router; } private String title; private ExecutorRouter router; public String getTitle() { return title; } public ExecutorRouter getRouter() { return router; } public static ExecutorRouteStrategyEnum match(String name, ExecutorRouteStrategyEnum defaultItem) { if (name != null) { for (ExecutorRouteStrategyEnum item : ExecutorRouteStrategyEnum.values()) { if (item.name().equals(name)) { return item; } } } return defaultItem; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouter.java ================================================ package com.xxl.job.admin.core.route; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; /** * Created by xuxueli on 17/3/10. */ public abstract class ExecutorRouter { protected static Logger logger = LoggerFactory.getLogger(ExecutorRouter.class); /** * route address * * @param addressList * @return ReturnT.content=address */ public abstract ReturnT route(TriggerParam triggerParam, List addressList); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.scheduler.XxlJobScheduler; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.IdleBeatParam; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.List; /** * Created by xuxueli on 17/3/10. */ public class ExecutorRouteBusyover extends ExecutorRouter { @Override public ReturnT route(TriggerParam triggerParam, List addressList) { StringBuffer idleBeatResultSB = new StringBuffer(); for (String address : addressList) { // beat ReturnT idleBeatResult = null; try { ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address); idleBeatResult = executorBiz.idleBeat(new IdleBeatParam(triggerParam.getJobId())); } catch (Exception e) { logger.error(e.getMessage(), e); idleBeatResult = new ReturnT(ReturnT.FAIL_CODE, "" + e); } idleBeatResultSB.append((idleBeatResultSB.length() > 0) ? "

" : "") .append(I18nUtil.getString("jobconf_idleBeat") + ":") .append("
address:").append(address) .append("
code:").append(idleBeatResult.getCode()) .append("
msg:").append(idleBeatResult.getMsg()); // beat success if (idleBeatResult.getCode() == ReturnT.SUCCESS_CODE) { idleBeatResult.setMsg(idleBeatResultSB.toString()); idleBeatResult.setContent(address); return idleBeatResult; } } return new ReturnT(ReturnT.FAIL_CODE, idleBeatResultSB.toString()); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteConsistentHash.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; /** * 分组下机器地址相同,不同JOB均匀散列在不同机器上,保证分组下机器分配JOB平均;且每个JOB固定调度其中一台机器; * a、virtual node:解决不均衡问题 * b、hash method replace hashCode:String的hashCode可能重复,需要进一步扩大hashCode的取值范围 * Created by xuxueli on 17/3/10. */ public class ExecutorRouteConsistentHash extends ExecutorRouter { private static int VIRTUAL_NODE_NUM = 100; /** * get hash code on 2^32 ring (md5散列的方式计算hash值) * * @param key * @return */ private static long hash(String key) { // md5 byte MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 not supported", e); } md5.reset(); byte[] keyBytes = null; try { keyBytes = key.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unknown string :" + key, e); } md5.update(keyBytes); byte[] digest = md5.digest(); // hash code, Truncate to 32-bits long hashCode = ((long) (digest[3] & 0xFF) << 24) | ((long) (digest[2] & 0xFF) << 16) | ((long) (digest[1] & 0xFF) << 8) | (digest[0] & 0xFF); long truncateHashCode = hashCode & 0xffffffffL; return truncateHashCode; } public String hashJob(int jobId, List addressList) { // ------A1------A2-------A3------ // -----------J1------------------ TreeMap addressRing = new TreeMap(); for (String address : addressList) { for (int i = 0; i < VIRTUAL_NODE_NUM; i++) { long addressHash = hash("SHARD-" + address + "-NODE-" + i); addressRing.put(addressHash, address); } } long jobHash = hash(String.valueOf(jobId)); SortedMap lastRing = addressRing.tailMap(jobHash); if (!lastRing.isEmpty()) { return lastRing.get(lastRing.firstKey()); } return addressRing.firstEntry().getValue(); } @Override public ReturnT route(TriggerParam triggerParam, List addressList) { String address = hashJob(triggerParam.getJobId(), addressList); return new ReturnT(address); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.scheduler.XxlJobScheduler; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.List; /** * Created by xuxueli on 17/3/10. */ public class ExecutorRouteFailover extends ExecutorRouter { @Override public ReturnT route(TriggerParam triggerParam, List addressList) { StringBuffer beatResultSB = new StringBuffer(); for (String address : addressList) { // beat ReturnT beatResult = null; try { ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address); beatResult = executorBiz.beat(); } catch (Exception e) { logger.error(e.getMessage(), e); beatResult = new ReturnT(ReturnT.FAIL_CODE, "" + e); } beatResultSB.append((beatResultSB.length() > 0) ? "

" : "") .append(I18nUtil.getString("jobconf_beat") + ":") .append("
address:").append(address) .append("
code:").append(beatResult.getCode()) .append("
msg:").append(beatResult.getMsg()); // beat success if (beatResult.getCode() == ReturnT.SUCCESS_CODE) { beatResult.setMsg(beatResultSB.toString()); beatResult.setContent(address); return beatResult; } } return new ReturnT(ReturnT.FAIL_CODE, beatResultSB.toString()); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFirst.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.List; /** * Created by xuxueli on 17/3/10. */ public class ExecutorRouteFirst extends ExecutorRouter { @Override public ReturnT route(TriggerParam triggerParam, List addressList) { return new ReturnT(addressList.get(0)); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteLFU.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * 单个JOB对应的每个执行器,使用频率最低的优先被选举 * a(*)、LFU(Least Frequently Used):最不经常使用,频率/次数 * b、LRU(Least Recently Used):最近最久未使用,时间 *

* Created by xuxueli on 17/3/10. */ public class ExecutorRouteLFU extends ExecutorRouter { private static ConcurrentMap> jobLfuMap = new ConcurrentHashMap>(); private static long CACHE_VALID_TIME = 0; public String route(int jobId, List addressList) { // cache clear if (System.currentTimeMillis() > CACHE_VALID_TIME) { jobLfuMap.clear(); CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24; } // lfu item init HashMap lfuItemMap = jobLfuMap.get(jobId); // Key排序可以用TreeMap+构造入参Compare;Value排序暂时只能通过ArrayList; if (lfuItemMap == null) { lfuItemMap = new HashMap(); jobLfuMap.putIfAbsent(jobId, lfuItemMap); // 避免重复覆盖 } // put new for (String address : addressList) { if (!lfuItemMap.containsKey(address) || lfuItemMap.get(address) > 1000000) { lfuItemMap.put(address, new Random().nextInt(addressList.size())); // 初始化时主动Random一次,缓解首次压力 } } // remove old List delKeys = new ArrayList<>(); for (String existKey : lfuItemMap.keySet()) { if (!addressList.contains(existKey)) { delKeys.add(existKey); } } if (delKeys.size() > 0) { for (String delKey : delKeys) { lfuItemMap.remove(delKey); } } // load least userd count address List> lfuItemList = new ArrayList>(lfuItemMap.entrySet()); Collections.sort(lfuItemList, new Comparator>() { @Override public int compare(Map.Entry o1, Map.Entry o2) { return o1.getValue().compareTo(o2.getValue()); } }); Map.Entry addressItem = lfuItemList.get(0); String minAddress = addressItem.getKey(); addressItem.setValue(addressItem.getValue() + 1); return addressItem.getKey(); } @Override public ReturnT route(TriggerParam triggerParam, List addressList) { String address = route(triggerParam.getJobId(), addressList); return new ReturnT(address); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteLRU.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * 单个JOB对应的每个执行器,最久为使用的优先被选举 * a、LFU(Least Frequently Used):最不经常使用,频率/次数 * b(*)、LRU(Least Recently Used):最近最久未使用,时间 *

* Created by xuxueli on 17/3/10. */ public class ExecutorRouteLRU extends ExecutorRouter { private static ConcurrentMap> jobLRUMap = new ConcurrentHashMap>(); private static long CACHE_VALID_TIME = 0; public String route(int jobId, List addressList) { // cache clear if (System.currentTimeMillis() > CACHE_VALID_TIME) { jobLRUMap.clear(); CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24; } // init lru LinkedHashMap lruItem = jobLRUMap.get(jobId); if (lruItem == null) { /** * LinkedHashMap * a、accessOrder:true=访问顺序排序(get/put时排序);false=插入顺序排期; * b、removeEldestEntry:新增元素时将会调用,返回true时会删除最老元素;可封装LinkedHashMap并重写该方法,比如定义最大容量,超出是返回true即可实现固定长度的LRU算法; */ lruItem = new LinkedHashMap(16, 0.75f, true); jobLRUMap.putIfAbsent(jobId, lruItem); } // put new for (String address : addressList) { if (!lruItem.containsKey(address)) { lruItem.put(address, address); } } // remove old List delKeys = new ArrayList<>(); for (String existKey : lruItem.keySet()) { if (!addressList.contains(existKey)) { delKeys.add(existKey); } } if (delKeys.size() > 0) { for (String delKey : delKeys) { lruItem.remove(delKey); } } // load String eldestKey = lruItem.entrySet().iterator().next().getKey(); String eldestValue = lruItem.get(eldestKey); return eldestValue; } @Override public ReturnT route(TriggerParam triggerParam, List addressList) { String address = route(triggerParam.getJobId(), addressList); return new ReturnT(address); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteLast.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.List; /** * Created by xuxueli on 17/3/10. */ public class ExecutorRouteLast extends ExecutorRouter { @Override public ReturnT route(TriggerParam triggerParam, List addressList) { return new ReturnT(addressList.get(addressList.size() - 1)); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteRandom.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.List; import java.util.Random; /** * Created by xuxueli on 17/3/10. */ public class ExecutorRouteRandom extends ExecutorRouter { private static Random localRandom = new Random(); @Override public ReturnT route(TriggerParam triggerParam, List addressList) { String address = addressList.get(localRandom.nextInt(addressList.size())); return new ReturnT(address); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteRound.java ================================================ package com.xxl.job.admin.core.route.strategy; import com.xxl.job.admin.core.route.ExecutorRouter; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import java.util.List; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; /** * Created by xuxueli on 17/3/10. */ public class ExecutorRouteRound extends ExecutorRouter { private static ConcurrentMap routeCountEachJob = new ConcurrentHashMap<>(); private static long CACHE_VALID_TIME = 0; private static int count(int jobId) { // cache clear if (System.currentTimeMillis() > CACHE_VALID_TIME) { routeCountEachJob.clear(); CACHE_VALID_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24; } AtomicInteger count = routeCountEachJob.get(jobId); if (count == null || count.get() > 1000000) { // 初始化时主动Random一次,缓解首次压力 count = new AtomicInteger(new Random().nextInt(100)); } else { // count++ count.addAndGet(1); } routeCountEachJob.put(jobId, count); return count.get(); } @Override public ReturnT route(TriggerParam triggerParam, List addressList) { String address = addressList.get(count(triggerParam.getJobId()) % addressList.size()); return new ReturnT(address); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/scheduler/MisfireStrategyEnum.java ================================================ package com.xxl.job.admin.core.scheduler; import com.xxl.job.admin.core.util.I18nUtil; /** * @author xuxueli 2020-10-29 21:11:23 */ public enum MisfireStrategyEnum { /** * do nothing */ DO_NOTHING(I18nUtil.getString("misfire_strategy_do_nothing")), /** * fire once now */ FIRE_ONCE_NOW(I18nUtil.getString("misfire_strategy_fire_once_now")); private String title; MisfireStrategyEnum(String title) { this.title = title; } public String getTitle() { return title; } public static MisfireStrategyEnum match(String name, MisfireStrategyEnum defaultItem) { for (MisfireStrategyEnum item : MisfireStrategyEnum.values()) { if (item.name().equals(name)) { return item; } } return defaultItem; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/scheduler/ScheduleTypeEnum.java ================================================ package com.xxl.job.admin.core.scheduler; import com.xxl.job.admin.core.util.I18nUtil; /** * @author xuxueli 2020-10-29 21:11:23 */ public enum ScheduleTypeEnum { NONE(I18nUtil.getString("schedule_type_none")), /** * schedule by cron */ CRON(I18nUtil.getString("schedule_type_cron")), /** * schedule by fixed rate (in seconds) */ FIX_RATE(I18nUtil.getString("schedule_type_fix_rate")), /** * schedule by fix delay (in seconds), after the last time */ /*FIX_DELAY(I18nUtil.getString("schedule_type_fix_delay"))*/; private String title; ScheduleTypeEnum(String title) { this.title = title; } public String getTitle() { return title; } public static ScheduleTypeEnum match(String name, ScheduleTypeEnum defaultItem) { for (ScheduleTypeEnum item : ScheduleTypeEnum.values()) { if (item.name().equals(name)) { return item; } } return defaultItem; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/scheduler/XxlJobScheduler.java ================================================ package com.xxl.job.admin.core.scheduler; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.thread.*; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.client.ExecutorBizClient; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * @author xuxueli 2018-10-28 00:18:17 */ public class XxlJobScheduler { private static final Logger logger = LoggerFactory.getLogger(XxlJobScheduler.class); public void init() throws Exception { // init i18n initI18n(); // admin trigger pool start JobTriggerPoolHelper.toStart(); // admin registry monitor run JobRegistryHelper.getInstance().start(); // admin fail-monitor run JobFailMonitorHelper.getInstance().start(); // admin lose-monitor run ( depend on JobTriggerPoolHelper ) JobCompleteHelper.getInstance().start(); // admin log report start JobLogReportHelper.getInstance().start(); // start-schedule ( depend on JobTriggerPoolHelper ) JobScheduleHelper.getInstance().start(); logger.info(">>>>>>>>> init xxl-job admin success."); } public void destroy() throws Exception { // stop-schedule JobScheduleHelper.getInstance().toStop(); // admin log report stop JobLogReportHelper.getInstance().toStop(); // admin lose-monitor stop JobCompleteHelper.getInstance().toStop(); // admin fail-monitor stop JobFailMonitorHelper.getInstance().toStop(); // admin registry stop JobRegistryHelper.getInstance().toStop(); // admin trigger pool stop JobTriggerPoolHelper.toStop(); } // ---------------------- I18n ---------------------- private void initI18n() { for (ExecutorBlockStrategyEnum item : ExecutorBlockStrategyEnum.values()) { item.setTitle(I18nUtil.getString("jobconf_block_".concat(item.name()))); } } // ---------------------- executor-client ---------------------- private static ConcurrentMap executorBizRepository = new ConcurrentHashMap(); public static ExecutorBiz getExecutorBiz(String address) throws Exception { // valid if (address == null || address.trim().length() == 0) { return null; } // load-cache address = address.trim(); ExecutorBiz executorBiz = executorBizRepository.get(address); if (executorBiz != null) { return executorBiz; } // set-cache executorBiz = new ExecutorBizClient(address, XxlJobAdminConfig.getAdminConfig().getAccessToken()); executorBizRepository.put(address, executorBiz); return executorBiz; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobCompleteHelper.java ================================================ package com.xxl.job.admin.core.thread; import com.xxl.job.admin.core.complete.XxlJobCompleter; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.model.HandleCallbackParam; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.util.DateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; import java.util.List; import java.util.concurrent.*; /** * job lose-monitor instance * * @author xuxueli 2015-9-1 18:05:56 */ public class JobCompleteHelper { private static Logger logger = LoggerFactory.getLogger(JobCompleteHelper.class); private static JobCompleteHelper instance = new JobCompleteHelper(); public static JobCompleteHelper getInstance() { return instance; } // ---------------------- monitor ---------------------- private ThreadPoolExecutor callbackThreadPool = null; private Thread monitorThread; private volatile boolean toStop = false; public void start() { // for callback callbackThreadPool = new ThreadPoolExecutor( 2, 20, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue(3000), new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "xxl-job, admin JobLosedMonitorHelper-callbackThreadPool-" + r.hashCode()); } }, new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { r.run(); logger.warn(">>>>>>>>>>> xxl-job, callback too fast, match threadpool rejected handler(run now)."); } }); // for monitor monitorThread = new Thread(new Runnable() { @Override public void run() { // wait for JobTriggerPoolHelper-init try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) { if (!toStop) { logger.error(e.getMessage(), e); } } // monitor while (!toStop) { try { // 任务结果丢失处理:调度记录停留在 "运行中" 状态超过10min,且对应执行器心跳注册失败不在线,则将本地调度主动标记失败; Date losedTime = DateUtil.addMinutes(new Date(), -10); List losedJobIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findLostJobIds(losedTime); if (losedJobIds != null && losedJobIds.size() > 0) { for (Long logId : losedJobIds) { XxlJobLog jobLog = new XxlJobLog(); jobLog.setId(logId); jobLog.setHandleTime(new Date()); jobLog.setHandleCode(ReturnT.FAIL_CODE); jobLog.setHandleMsg(I18nUtil.getString("joblog_lost_fail")); XxlJobCompleter.updateHandleInfoAndFinish(jobLog); } } } catch (Exception e) { if (!toStop) { logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e); } } try { TimeUnit.SECONDS.sleep(60); } catch (Exception e) { if (!toStop) { logger.error(e.getMessage(), e); } } } logger.info(">>>>>>>>>>> xxl-job, JobLosedMonitorHelper stop"); } }); monitorThread.setDaemon(true); monitorThread.setName("xxl-job, admin JobLosedMonitorHelper"); monitorThread.start(); } public void toStop() { toStop = true; // stop registryOrRemoveThreadPool callbackThreadPool.shutdownNow(); // stop monitorThread (interrupt and wait) monitorThread.interrupt(); try { monitorThread.join(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } // ---------------------- helper ---------------------- public ReturnT callback(List callbackParamList) { callbackThreadPool.execute(new Runnable() { @Override public void run() { for (HandleCallbackParam handleCallbackParam : callbackParamList) { ReturnT callbackResult = callback(handleCallbackParam); logger.debug(">>>>>>>>> JobApiController.callback {}, handleCallbackParam={}, callbackResult={}", (callbackResult.getCode() == ReturnT.SUCCESS_CODE ? "success" : "fail"), handleCallbackParam, callbackResult); } } }); return ReturnT.SUCCESS; } private ReturnT callback(HandleCallbackParam handleCallbackParam) { // valid log item XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(handleCallbackParam.getLogId()); if (log == null) { return new ReturnT(ReturnT.FAIL_CODE, "log item not found."); } if (log.getHandleCode() > 0) { return new ReturnT(ReturnT.FAIL_CODE, "log repeate callback."); // avoid repeat callback, trigger child job etc } // handle msg StringBuffer handleMsg = new StringBuffer(); if (log.getHandleMsg() != null) { handleMsg.append(log.getHandleMsg()).append("
"); } if (handleCallbackParam.getHandleMsg() != null) { handleMsg.append(handleCallbackParam.getHandleMsg()); } // success, save log log.setHandleTime(new Date()); log.setHandleCode(handleCallbackParam.getHandleCode()); log.setHandleMsg(handleMsg.toString()); XxlJobCompleter.updateHandleInfoAndFinish(log); return ReturnT.SUCCESS; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java ================================================ package com.xxl.job.admin.core.thread; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.trigger.TriggerTypeEnum; import com.xxl.job.admin.core.util.I18nUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.TimeUnit; /** * job monitor instance * * @author xuxueli 2015-9-1 18:05:56 */ public class JobFailMonitorHelper { private static Logger logger = LoggerFactory.getLogger(JobFailMonitorHelper.class); private static JobFailMonitorHelper instance = new JobFailMonitorHelper(); public static JobFailMonitorHelper getInstance() { return instance; } // ---------------------- monitor ---------------------- private Thread monitorThread; private volatile boolean toStop = false; public void start() { monitorThread = new Thread(new Runnable() { @Override public void run() { // monitor while (!toStop) { try { List failLogIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findFailJobLogIds(1000); if (failLogIds != null && !failLogIds.isEmpty()) { for (long failLogId : failLogIds) { // lock log int lockRet = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, 0, -1); if (lockRet < 1) { continue; } XxlJobLog log = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().load(failLogId); XxlJobInfo info = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(log.getJobId()); // 1、fail retry monitor if (log.getExecutorFailRetryCount() > 0) { JobTriggerPoolHelper.trigger(log.getJobId(), TriggerTypeEnum.RETRY, (log.getExecutorFailRetryCount() - 1), log.getExecutorShardingParam(), log.getExecutorParam(), null); String retryMsg = "

>>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_type_retry") + "<<<<<<<<<<<
"; log.setTriggerMsg(log.getTriggerMsg() + retryMsg); XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(log); } // 2、fail alarm monitor int newAlarmStatus = 0; // 告警状态:0-默认、-1=锁定状态、1-无需告警、2-告警成功、3-告警失败 if (info != null) { boolean alarmResult = XxlJobAdminConfig.getAdminConfig().getJobAlarmer().alarm(info, log); newAlarmStatus = alarmResult ? 2 : 3; } else { newAlarmStatus = 1; } XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateAlarmStatus(failLogId, -1, newAlarmStatus); } } } catch (Exception e) { if (!toStop) { logger.error(">>>>>>>>>>> xxl-job, job fail monitor thread error:{}", e); } } try { TimeUnit.SECONDS.sleep(10); } catch (Exception e) { if (!toStop) { logger.error(e.getMessage(), e); } } } logger.info(">>>>>>>>>>> xxl-job, job fail monitor thread stop"); } }); monitorThread.setDaemon(true); monitorThread.setName("xxl-job, admin JobFailMonitorHelper"); monitorThread.start(); } public void toStop() { toStop = true; // interrupt and wait monitorThread.interrupt(); try { monitorThread.join(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobLogReportHelper.java ================================================ package com.xxl.job.admin.core.thread; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.model.XxlJobLogReport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; /** * job log report helper * * @author xuxueli 2019-11-22 */ public class JobLogReportHelper { private static Logger logger = LoggerFactory.getLogger(JobLogReportHelper.class); private static JobLogReportHelper instance = new JobLogReportHelper(); public static JobLogReportHelper getInstance() { return instance; } private Thread logrThread; private volatile boolean toStop = false; public void start() { logrThread = new Thread(new Runnable() { @Override public void run() { // last clean log time long lastCleanLogTime = 0; while (!toStop) { // 1、log-report refresh: refresh log report in 3 days try { for (int i = 0; i < 3; i++) { // today Calendar itemDay = Calendar.getInstance(); itemDay.add(Calendar.DAY_OF_MONTH, -i); itemDay.set(Calendar.HOUR_OF_DAY, 0); itemDay.set(Calendar.MINUTE, 0); itemDay.set(Calendar.SECOND, 0); itemDay.set(Calendar.MILLISECOND, 0); Date todayFrom = itemDay.getTime(); itemDay.set(Calendar.HOUR_OF_DAY, 23); itemDay.set(Calendar.MINUTE, 59); itemDay.set(Calendar.SECOND, 59); itemDay.set(Calendar.MILLISECOND, 999); Date todayTo = itemDay.getTime(); // refresh log-report every minute XxlJobLogReport xxlJobLogReport = new XxlJobLogReport(); xxlJobLogReport.setTriggerDay(todayFrom); xxlJobLogReport.setRunningCount(0); xxlJobLogReport.setSucCount(0); xxlJobLogReport.setFailCount(0); Map triggerCountMap = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findLogReport(todayFrom, todayTo); if (triggerCountMap != null && triggerCountMap.size() > 0) { int triggerDayCount = triggerCountMap.containsKey("triggerDayCount") ? Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCount"))) : 0; int triggerDayCountRunning = triggerCountMap.containsKey("triggerDayCountRunning") ? Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCountRunning"))) : 0; int triggerDayCountSuc = triggerCountMap.containsKey("triggerDayCountSuc") ? Integer.valueOf(String.valueOf(triggerCountMap.get("triggerDayCountSuc"))) : 0; int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc; xxlJobLogReport.setRunningCount(triggerDayCountRunning); xxlJobLogReport.setSucCount(triggerDayCountSuc); xxlJobLogReport.setFailCount(triggerDayCountFail); } // do refresh int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobLogReportDao().update(xxlJobLogReport); if (ret < 1) { XxlJobAdminConfig.getAdminConfig().getXxlJobLogReportDao().save(xxlJobLogReport); } } } catch (Exception e) { if (!toStop) { logger.error(">>>>>>>>>>> xxl-job, job log report thread error:{}", e); } } // 2、log-clean: switch open & once each day if (XxlJobAdminConfig.getAdminConfig().getLogretentiondays() > 0 && System.currentTimeMillis() - lastCleanLogTime > 24 * 60 * 60 * 1000) { // expire-time Calendar expiredDay = Calendar.getInstance(); expiredDay.add(Calendar.DAY_OF_MONTH, -1 * XxlJobAdminConfig.getAdminConfig().getLogretentiondays()); expiredDay.set(Calendar.HOUR_OF_DAY, 0); expiredDay.set(Calendar.MINUTE, 0); expiredDay.set(Calendar.SECOND, 0); expiredDay.set(Calendar.MILLISECOND, 0); Date clearBeforeTime = expiredDay.getTime(); // clean expired log List logIds = null; do { logIds = XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().findClearLogIds(0, 0, clearBeforeTime, 0, 1000); if (logIds != null && logIds.size() > 0) { XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().clearLog(logIds); } } while (logIds != null && logIds.size() > 0); // update clean time lastCleanLogTime = System.currentTimeMillis(); } try { TimeUnit.MINUTES.sleep(1); } catch (Exception e) { if (!toStop) { logger.error(e.getMessage(), e); } } } logger.info(">>>>>>>>>>> xxl-job, job log report thread stop"); } }); logrThread.setDaemon(true); logrThread.setName("xxl-job, admin JobLogReportHelper"); logrThread.start(); } public void toStop() { toStop = true; // interrupt and wait logrThread.interrupt(); try { logrThread.join(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobRegistryHelper.java ================================================ package com.xxl.job.admin.core.thread; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobRegistry; import com.xxl.job.core.biz.model.RegistryParam; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.enums.RegistryConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import java.util.*; import java.util.concurrent.*; /** * job registry instance * * @author xuxueli 2016-10-02 19:10:24 */ public class JobRegistryHelper { private static Logger logger = LoggerFactory.getLogger(JobRegistryHelper.class); private static JobRegistryHelper instance = new JobRegistryHelper(); public static JobRegistryHelper getInstance() { return instance; } private ThreadPoolExecutor registryOrRemoveThreadPool = null; private Thread registryMonitorThread; private volatile boolean toStop = false; public void start() { // for registry or remove registryOrRemoveThreadPool = new ThreadPoolExecutor( 2, 10, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue(2000), new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "xxl-job, admin JobRegistryMonitorHelper-registryOrRemoveThreadPool-" + r.hashCode()); } }, new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { r.run(); logger.warn(">>>>>>>>>>> xxl-job, registry or remove too fast, match threadpool rejected handler(run now)."); } }); // for monitor registryMonitorThread = new Thread(new Runnable() { @Override public void run() { while (!toStop) { try { // auto registry group List groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0); if (groupList != null && !groupList.isEmpty()) { // remove dead address (admin/executor) List ids = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findDead(RegistryConfig.DEAD_TIMEOUT, new Date()); if (ids != null && ids.size() > 0) { XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids); } // fresh online address (admin/executor) HashMap> appAddressMap = new HashMap>(); List list = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findAll(RegistryConfig.DEAD_TIMEOUT, new Date()); if (list != null) { for (XxlJobRegistry item : list) { if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) { String appname = item.getRegistryKey(); List registryList = appAddressMap.get(appname); if (registryList == null) { registryList = new ArrayList(); } if (!registryList.contains(item.getRegistryValue())) { registryList.add(item.getRegistryValue()); } appAddressMap.put(appname, registryList); } } } // fresh group address for (XxlJobGroup group : groupList) { List registryList = appAddressMap.get(group.getAppname()); String addressListStr = null; if (registryList != null && !registryList.isEmpty()) { Collections.sort(registryList); StringBuilder addressListSB = new StringBuilder(); for (String item : registryList) { addressListSB.append(item).append(","); } addressListStr = addressListSB.toString(); addressListStr = addressListStr.substring(0, addressListStr.length() - 1); } group.setAddressList(addressListStr); group.setUpdateTime(new Date()); XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group); } } } catch (Exception e) { if (!toStop) { logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e); } } try { TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT); } catch (InterruptedException e) { if (!toStop) { logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e); } } } logger.info(">>>>>>>>>>> xxl-job, job registry monitor thread stop"); } }); registryMonitorThread.setDaemon(true); registryMonitorThread.setName("xxl-job, admin JobRegistryMonitorHelper-registryMonitorThread"); registryMonitorThread.start(); } public void toStop() { toStop = true; // stop registryOrRemoveThreadPool registryOrRemoveThreadPool.shutdownNow(); // stop monitir (interrupt and wait) registryMonitorThread.interrupt(); try { registryMonitorThread.join(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } // ---------------------- helper ---------------------- public ReturnT registry(RegistryParam registryParam) { // valid if (!StringUtils.hasText(registryParam.getRegistryGroup()) || !StringUtils.hasText(registryParam.getRegistryKey()) || !StringUtils.hasText(registryParam.getRegistryValue())) { return new ReturnT(ReturnT.FAIL_CODE, "Illegal Argument."); } // async execute registryOrRemoveThreadPool.execute(new Runnable() { @Override public void run() { int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date()); if (ret < 1) { XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date()); // fresh freshGroupRegistryInfo(registryParam); } } }); return ReturnT.SUCCESS; } public ReturnT registryRemove(RegistryParam registryParam) { // valid if (!StringUtils.hasText(registryParam.getRegistryGroup()) || !StringUtils.hasText(registryParam.getRegistryKey()) || !StringUtils.hasText(registryParam.getRegistryValue())) { return new ReturnT(ReturnT.FAIL_CODE, "Illegal Argument."); } // async execute registryOrRemoveThreadPool.execute(new Runnable() { @Override public void run() { int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryDelete(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue()); if (ret > 0) { // fresh freshGroupRegistryInfo(registryParam); } } }); return ReturnT.SUCCESS; } private void freshGroupRegistryInfo(RegistryParam registryParam) { // Under consideration, prevent affecting core tables } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobScheduleHelper.java ================================================ package com.xxl.job.admin.core.thread; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.cron.CronExpression; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.core.trigger.TriggerTypeEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** * @author xuxueli 2019-05-21 */ public class JobScheduleHelper { private static Logger logger = LoggerFactory.getLogger(JobScheduleHelper.class); private static JobScheduleHelper instance = new JobScheduleHelper(); public static JobScheduleHelper getInstance() { return instance; } public static final long PRE_READ_MS = 5000; // pre read private Thread scheduleThread; private Thread ringThread; private volatile boolean scheduleThreadToStop = false; private volatile boolean ringThreadToStop = false; private volatile static Map> ringData = new ConcurrentHashMap<>(); public void start() { // schedule thread scheduleThread = new Thread(new Runnable() { @Override public void run() { try { TimeUnit.MILLISECONDS.sleep(5000 - System.currentTimeMillis() % 1000); } catch (InterruptedException e) { if (!scheduleThreadToStop) { logger.error(e.getMessage(), e); } } logger.info(">>>>>>>>> init xxl-job admin scheduler success."); // pre-read count: treadpool-size * trigger-qps (each trigger cost 50ms, qps = 1000/50 = 20) int preReadCount = (XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax() + XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax()) * 20; while (!scheduleThreadToStop) { // Scan Job long start = System.currentTimeMillis(); Connection conn = null; Boolean connAutoCommit = null; PreparedStatement preparedStatement = null; boolean preReadSuc = true; try { conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection(); connAutoCommit = conn.getAutoCommit(); conn.setAutoCommit(false); preparedStatement = conn.prepareStatement("select * from xxl_job_lock where lock_name = 'schedule_lock' for update"); preparedStatement.execute(); // tx start // 1、pre read long nowTime = System.currentTimeMillis(); List scheduleList = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount); if (scheduleList != null && scheduleList.size() > 0) { // 2、push time-ring for (XxlJobInfo jobInfo : scheduleList) { // time-ring jump if (nowTime > jobInfo.getTriggerNextTime() + PRE_READ_MS) { // 2.1、trigger-expire > 5s:pass && make next-trigger-time logger.warn(">>>>>>>>>>> xxl-job, schedule misfire, jobId = " + jobInfo.getId()); // 1、misfire match MisfireStrategyEnum misfireStrategyEnum = MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), MisfireStrategyEnum.DO_NOTHING); if (MisfireStrategyEnum.FIRE_ONCE_NOW == misfireStrategyEnum) { // FIRE_ONCE_NOW 》 trigger JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.MISFIRE, -1, null, null, null); logger.debug(">>>>>>>>>>> xxl-job, schedule push trigger : jobId = " + jobInfo.getId()); } // 2、fresh next refreshNextValidTime(jobInfo, new Date()); } else if (nowTime > jobInfo.getTriggerNextTime()) { // 2.2、trigger-expire < 5s:direct-trigger && make next-trigger-time // 1、trigger JobTriggerPoolHelper.trigger(jobInfo.getId(), TriggerTypeEnum.CRON, -1, null, null, null); logger.debug(">>>>>>>>>>> xxl-job, schedule push trigger : jobId = " + jobInfo.getId()); // 2、fresh next refreshNextValidTime(jobInfo, new Date()); // next-trigger-time in 5s, pre-read again if (jobInfo.getTriggerStatus() == 1 && nowTime + PRE_READ_MS > jobInfo.getTriggerNextTime()) { // 1、make ring second int ringSecond = (int) ((jobInfo.getTriggerNextTime() / 1000) % 60); // 2、push time ring pushTimeRing(ringSecond, jobInfo.getId()); // 3、fresh next refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime())); } } else { // 2.3、trigger-pre-read:time-ring trigger && make next-trigger-time // 1、make ring second int ringSecond = (int) ((jobInfo.getTriggerNextTime() / 1000) % 60); // 2、push time ring pushTimeRing(ringSecond, jobInfo.getId()); // 3、fresh next refreshNextValidTime(jobInfo, new Date(jobInfo.getTriggerNextTime())); } } // 3、update trigger info for (XxlJobInfo jobInfo : scheduleList) { XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().scheduleUpdate(jobInfo); } } else { preReadSuc = false; } // tx stop } catch (Exception e) { if (!scheduleThreadToStop) { logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread error:{}", e); } } finally { // commit if (conn != null) { try { conn.commit(); } catch (SQLException e) { if (!scheduleThreadToStop) { logger.error(e.getMessage(), e); } } try { conn.setAutoCommit(connAutoCommit); } catch (SQLException e) { if (!scheduleThreadToStop) { logger.error(e.getMessage(), e); } } try { conn.close(); } catch (SQLException e) { if (!scheduleThreadToStop) { logger.error(e.getMessage(), e); } } } // close PreparedStatement if (null != preparedStatement) { try { preparedStatement.close(); } catch (SQLException e) { if (!scheduleThreadToStop) { logger.error(e.getMessage(), e); } } } } long cost = System.currentTimeMillis() - start; // Wait seconds, align second if (cost < 1000) { // scan-overtime, not wait try { // pre-read period: success > scan each second; fail > skip this period; TimeUnit.MILLISECONDS.sleep((preReadSuc ? 1000 : PRE_READ_MS) - System.currentTimeMillis() % 1000); } catch (InterruptedException e) { if (!scheduleThreadToStop) { logger.error(e.getMessage(), e); } } } } logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper#scheduleThread stop"); } }); scheduleThread.setDaemon(true); scheduleThread.setName("xxl-job, admin JobScheduleHelper#scheduleThread"); scheduleThread.start(); // ring thread ringThread = new Thread(new Runnable() { @Override public void run() { while (!ringThreadToStop) { // align second try { TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000); } catch (InterruptedException e) { if (!ringThreadToStop) { logger.error(e.getMessage(), e); } } try { // second data List ringItemData = new ArrayList<>(); int nowSecond = Calendar.getInstance().get(Calendar.SECOND); // 避免处理耗时太长,跨过刻度,向前校验一个刻度; for (int i = 0; i < 2; i++) { List tmpData = ringData.remove((nowSecond + 60 - i) % 60); if (tmpData != null) { ringItemData.addAll(tmpData); } } // ring trigger logger.debug(">>>>>>>>>>> xxl-job, time-ring beat : " + nowSecond + " = " + Arrays.asList(ringItemData)); if (ringItemData.size() > 0) { // do trigger for (int jobId : ringItemData) { // do trigger JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null, null); } // clear ringItemData.clear(); } } catch (Exception e) { if (!ringThreadToStop) { logger.error(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread error:{}", e); } } } logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper#ringThread stop"); } }); ringThread.setDaemon(true); ringThread.setName("xxl-job, admin JobScheduleHelper#ringThread"); ringThread.start(); } private void refreshNextValidTime(XxlJobInfo jobInfo, Date fromTime) throws Exception { Date nextValidTime = generateNextValidTime(jobInfo, fromTime); if (nextValidTime != null) { jobInfo.setTriggerLastTime(jobInfo.getTriggerNextTime()); jobInfo.setTriggerNextTime(nextValidTime.getTime()); } else { jobInfo.setTriggerStatus(0); jobInfo.setTriggerLastTime(0); jobInfo.setTriggerNextTime(0); logger.warn(">>>>>>>>>>> xxl-job, refreshNextValidTime fail for job: jobId={}, scheduleType={}, scheduleConf={}", jobInfo.getId(), jobInfo.getScheduleType(), jobInfo.getScheduleConf()); } } private void pushTimeRing(int ringSecond, int jobId) { // push async ring List ringItemData = ringData.get(ringSecond); if (ringItemData == null) { ringItemData = new ArrayList(); ringData.put(ringSecond, ringItemData); } ringItemData.add(jobId); logger.debug(">>>>>>>>>>> xxl-job, schedule push time-ring : " + ringSecond + " = " + Arrays.asList(ringItemData)); } public void toStop() { // 1、stop schedule scheduleThreadToStop = true; try { TimeUnit.SECONDS.sleep(1); // wait } catch (InterruptedException e) { logger.error(e.getMessage(), e); } if (scheduleThread.getState() != Thread.State.TERMINATED) { // interrupt and wait scheduleThread.interrupt(); try { scheduleThread.join(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } // if has ring data boolean hasRingData = false; if (!ringData.isEmpty()) { for (int second : ringData.keySet()) { List tmpData = ringData.get(second); if (tmpData != null && tmpData.size() > 0) { hasRingData = true; break; } } } if (hasRingData) { try { TimeUnit.SECONDS.sleep(8); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } // stop ring (wait job-in-memory stop) ringThreadToStop = true; try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } if (ringThread.getState() != Thread.State.TERMINATED) { // interrupt and wait ringThread.interrupt(); try { ringThread.join(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } logger.info(">>>>>>>>>>> xxl-job, JobScheduleHelper stop"); } // ---------------------- tools ---------------------- public static Date generateNextValidTime(XxlJobInfo jobInfo, Date fromTime) throws Exception { ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null); if (ScheduleTypeEnum.CRON == scheduleTypeEnum) { Date nextValidTime = new CronExpression(jobInfo.getScheduleConf()).getNextValidTimeAfter(fromTime); return nextValidTime; } else if (ScheduleTypeEnum.FIX_RATE == scheduleTypeEnum /*|| ScheduleTypeEnum.FIX_DELAY == scheduleTypeEnum*/) { return new Date(fromTime.getTime() + Integer.valueOf(jobInfo.getScheduleConf()) * 1000); } return null; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobTriggerPoolHelper.java ================================================ package com.xxl.job.admin.core.thread; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.trigger.TriggerTypeEnum; import com.xxl.job.admin.core.trigger.XxlJobTrigger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * job trigger thread pool helper * * @author xuxueli 2018-07-03 21:08:07 */ public class JobTriggerPoolHelper { private static Logger logger = LoggerFactory.getLogger(JobTriggerPoolHelper.class); // ---------------------- trigger pool ---------------------- // fast/slow thread pool private ThreadPoolExecutor fastTriggerPool = null; private ThreadPoolExecutor slowTriggerPool = null; public void start() { fastTriggerPool = new ThreadPoolExecutor( 10, XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax(), 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(1000), new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-fastTriggerPool-" + r.hashCode()); } }); slowTriggerPool = new ThreadPoolExecutor( 10, XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax(), 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(2000), new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-slowTriggerPool-" + r.hashCode()); } }); } public void stop() { //triggerPool.shutdown(); fastTriggerPool.shutdownNow(); slowTriggerPool.shutdownNow(); logger.info(">>>>>>>>> xxl-job trigger thread pool shutdown success."); } // job timeout count private volatile long minTim = System.currentTimeMillis() / 60000; // ms > min private volatile ConcurrentMap jobTimeoutCountMap = new ConcurrentHashMap<>(); /** * add trigger */ public void addTrigger(final int jobId, final TriggerTypeEnum triggerType, final int failRetryCount, final String executorShardingParam, final String executorParam, final String addressList) { // choose thread pool ThreadPoolExecutor triggerPool_ = fastTriggerPool; AtomicInteger jobTimeoutCount = jobTimeoutCountMap.get(jobId); if (jobTimeoutCount != null && jobTimeoutCount.get() > 10) { // job-timeout 10 times in 1 min triggerPool_ = slowTriggerPool; } // trigger triggerPool_.execute(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); try { // do trigger XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { // check timeout-count-map long minTim_now = System.currentTimeMillis() / 60000; if (minTim != minTim_now) { minTim = minTim_now; jobTimeoutCountMap.clear(); } // incr timeout-count-map long cost = System.currentTimeMillis() - start; if (cost > 500) { // ob-timeout threshold 500ms AtomicInteger timeoutCount = jobTimeoutCountMap.putIfAbsent(jobId, new AtomicInteger(1)); if (timeoutCount != null) { timeoutCount.incrementAndGet(); } } } } }); } // ---------------------- helper ---------------------- private static JobTriggerPoolHelper helper = new JobTriggerPoolHelper(); public static void toStart() { helper.start(); } public static void toStop() { helper.stop(); } /** * @param jobId * @param triggerType * @param failRetryCount >=0: use this param * <0: use param from job info config * @param executorShardingParam * @param executorParam null: use job param * not null: cover job param */ public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam, String executorParam, String addressList) { helper.addTrigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam, addressList); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/TriggerTypeEnum.java ================================================ package com.xxl.job.admin.core.trigger; import com.xxl.job.admin.core.util.I18nUtil; /** * trigger type enum * * @author xuxueli 2018-09-16 04:56:41 */ public enum TriggerTypeEnum { MANUAL(I18nUtil.getString("jobconf_trigger_type_manual")), CRON(I18nUtil.getString("jobconf_trigger_type_cron")), RETRY(I18nUtil.getString("jobconf_trigger_type_retry")), PARENT(I18nUtil.getString("jobconf_trigger_type_parent")), API(I18nUtil.getString("jobconf_trigger_type_api")), MISFIRE(I18nUtil.getString("jobconf_trigger_type_misfire")); private TriggerTypeEnum(String title) { this.title = title; } private String title; public String getTitle() { return title; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java ================================================ package com.xxl.job.admin.core.trigger; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.core.scheduler.XxlJobScheduler; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.TriggerParam; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.util.IpUtil; import com.xxl.job.core.util.ThrowableUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; /** * xxl-job trigger * Created by xuxueli on 17/7/13. */ public class XxlJobTrigger { private static Logger logger = LoggerFactory.getLogger(XxlJobTrigger.class); /** * trigger job * * @param jobId * @param triggerType * @param failRetryCount >=0: use this param * <0: use param from job info config * @param executorShardingParam * @param executorParam null: use job param * not null: cover job param * @param addressList null: use executor addressList * not null: cover */ public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam, String executorParam, String addressList) { // load data XxlJobInfo jobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(jobId); if (jobInfo == null) { logger.warn(">>>>>>>>>>>> trigger fail, jobId invalid,jobId={}", jobId); return; } if (executorParam != null) { jobInfo.setExecutorParam(executorParam); } int finalFailRetryCount = failRetryCount >= 0 ? failRetryCount : jobInfo.getExecutorFailRetryCount(); XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(jobInfo.getJobGroup()); // cover addressList if (addressList != null && addressList.trim().length() > 0) { group.setAddressType(1); group.setAddressList(addressList.trim()); } // sharding param int[] shardingParam = null; if (executorShardingParam != null) { String[] shardingArr = executorShardingParam.split("/"); if (shardingArr.length == 2 && isNumeric(shardingArr[0]) && isNumeric(shardingArr[1])) { shardingParam = new int[2]; shardingParam[0] = Integer.valueOf(shardingArr[0]); shardingParam[1] = Integer.valueOf(shardingArr[1]); } } if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) && group.getRegistryList() != null && !group.getRegistryList().isEmpty() && shardingParam == null) { for (int i = 0; i < group.getRegistryList().size(); i++) { processTrigger(group, jobInfo, finalFailRetryCount, triggerType, i, group.getRegistryList().size()); } } else { if (shardingParam == null) { shardingParam = new int[]{0, 1}; } processTrigger(group, jobInfo, finalFailRetryCount, triggerType, shardingParam[0], shardingParam[1]); } } private static boolean isNumeric(String str) { try { int result = Integer.valueOf(str); return true; } catch (NumberFormatException e) { return false; } } /** * @param group job group, registry list may be empty * @param jobInfo * @param finalFailRetryCount * @param triggerType * @param index sharding index * @param total sharding index */ private static void processTrigger(XxlJobGroup group, XxlJobInfo jobInfo, int finalFailRetryCount, TriggerTypeEnum triggerType, int index, int total) { // param ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), ExecutorBlockStrategyEnum.SERIAL_EXECUTION); // block strategy ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null); // route strategy String shardingParam = (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) ? String.valueOf(index).concat("/").concat(String.valueOf(total)) : null; // 1、save log-id XxlJobLog jobLog = new XxlJobLog(); jobLog.setJobGroup(jobInfo.getJobGroup()); jobLog.setJobId(jobInfo.getId()); jobLog.setTriggerTime(new Date()); XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().save(jobLog); logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId()); // 2、init trigger-param TriggerParam triggerParam = new TriggerParam(); triggerParam.setJobId(jobInfo.getId()); triggerParam.setExecutorHandler(jobInfo.getExecutorHandler()); triggerParam.setExecutorParams(jobInfo.getExecutorParam()); triggerParam.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy()); triggerParam.setExecutorTimeout(jobInfo.getExecutorTimeout()); triggerParam.setLogId(jobLog.getId()); triggerParam.setLogDateTime(jobLog.getTriggerTime().getTime()); triggerParam.setGlueType(jobInfo.getGlueType()); triggerParam.setGlueSource(jobInfo.getGlueSource()); triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime()); triggerParam.setBroadcastIndex(index); triggerParam.setBroadcastTotal(total); // 3、init address String address = null; ReturnT routeAddressResult = null; if (group.getRegistryList() != null && !group.getRegistryList().isEmpty()) { if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) { if (index < group.getRegistryList().size()) { address = group.getRegistryList().get(index); } else { address = group.getRegistryList().get(0); } } else { routeAddressResult = executorRouteStrategyEnum.getRouter().route(triggerParam, group.getRegistryList()); if (routeAddressResult.getCode() == ReturnT.SUCCESS_CODE) { address = routeAddressResult.getContent(); } } } else { routeAddressResult = new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("jobconf_trigger_address_empty")); } // 4、trigger remote executor ReturnT triggerResult = null; if (address != null) { triggerResult = runExecutor(triggerParam, address); } else { triggerResult = new ReturnT(ReturnT.FAIL_CODE, null); } // 5、collection trigger info StringBuffer triggerMsgSb = new StringBuffer(); triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_type")).append(":").append(triggerType.getTitle()); triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":").append(IpUtil.getIp()); triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":") .append((group.getAddressType() == 0) ? I18nUtil.getString("jobgroup_field_addressType_0") : I18nUtil.getString("jobgroup_field_addressType_1")); triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":").append(group.getRegistryList()); triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":").append(executorRouteStrategyEnum.getTitle()); if (shardingParam != null) { triggerMsgSb.append("(" + shardingParam + ")"); } triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":").append(blockStrategy.getTitle()); triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_timeout")).append(":").append(jobInfo.getExecutorTimeout()); triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorFailRetryCount")).append(":").append(finalFailRetryCount); triggerMsgSb.append("

>>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_run") + "<<<<<<<<<<<
") .append((routeAddressResult != null && routeAddressResult.getMsg() != null) ? routeAddressResult.getMsg() + "

" : "").append(triggerResult.getMsg() != null ? triggerResult.getMsg() : ""); // 6、save log trigger-info jobLog.setExecutorAddress(address); jobLog.setExecutorHandler(jobInfo.getExecutorHandler()); jobLog.setExecutorParam(jobInfo.getExecutorParam()); jobLog.setExecutorShardingParam(shardingParam); jobLog.setExecutorFailRetryCount(finalFailRetryCount); //jobLog.setTriggerTime(); jobLog.setTriggerCode(triggerResult.getCode()); jobLog.setTriggerMsg(triggerMsgSb.toString()); XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(jobLog); logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId()); } /** * run executor * * @param triggerParam * @param address * @return */ public static ReturnT runExecutor(TriggerParam triggerParam, String address) { ReturnT runResult = null; try { ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(address); runResult = executorBiz.run(triggerParam); } catch (Exception e) { logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e); runResult = new ReturnT(ReturnT.FAIL_CODE, ThrowableUtil.toString(e)); } StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":"); runResultSB.append("
address:").append(address); runResultSB.append("
code:").append(runResult.getCode()); runResultSB.append("
msg:").append(runResult.getMsg()); runResult.setMsg(runResultSB.toString()); return runResult; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/CookieUtil.java ================================================ package com.xxl.job.admin.core.util; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Cookie.Util * * @author xuxueli 2015-12-12 18:01:06 */ public class CookieUtil { // 默认缓存时间,单位/秒, 2H private static final int COOKIE_MAX_AGE = Integer.MAX_VALUE; // 保存路径,根路径 private static final String COOKIE_PATH = "/"; /** * 保存 * * @param response * @param key * @param value * @param ifRemember */ public static void set(HttpServletResponse response, String key, String value, boolean ifRemember) { int age = ifRemember ? COOKIE_MAX_AGE : -1; set(response, key, value, null, COOKIE_PATH, age, true); } /** * 保存 * * @param response * @param key * @param value * @param maxAge */ private static void set(HttpServletResponse response, String key, String value, String domain, String path, int maxAge, boolean isHttpOnly) { Cookie cookie = new Cookie(key, value); if (domain != null) { cookie.setDomain(domain); } cookie.setPath(path); cookie.setMaxAge(maxAge); cookie.setHttpOnly(isHttpOnly); response.addCookie(cookie); } /** * 查询value * * @param request * @param key * @return */ public static String getValue(HttpServletRequest request, String key) { Cookie cookie = get(request, key); if (cookie != null) { return cookie.getValue(); } return null; } /** * 查询Cookie * * @param request * @param key */ private static Cookie get(HttpServletRequest request, String key) { Cookie[] arr_cookie = request.getCookies(); if (arr_cookie != null && arr_cookie.length > 0) { for (Cookie cookie : arr_cookie) { if (cookie.getName().equals(key)) { return cookie; } } } return null; } /** * 删除Cookie * * @param request * @param response * @param key */ public static void remove(HttpServletRequest request, HttpServletResponse response, String key) { Cookie cookie = get(request, key); if (cookie != null) { set(response, key, "", null, COOKIE_PATH, 0, true); } } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/FtlUtil.java ================================================ package com.xxl.job.admin.core.util; import freemarker.ext.beans.BeansWrapper; import freemarker.ext.beans.BeansWrapperBuilder; import freemarker.template.Configuration; import freemarker.template.TemplateHashModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * ftl util * * @author xuxueli 2018-01-17 20:37:48 */ public class FtlUtil { private static Logger logger = LoggerFactory.getLogger(FtlUtil.class); private static BeansWrapper wrapper = new BeansWrapperBuilder(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).build(); //BeansWrapper.getDefaultInstance(); public static TemplateHashModel generateStaticModel(String packageName) { try { TemplateHashModel staticModels = wrapper.getStaticModels(); TemplateHashModel fileStatics = (TemplateHashModel) staticModels.get(packageName); return fileStatics; } catch (Exception e) { logger.error(e.getMessage(), e); } return null; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java ================================================ package com.xxl.job.admin.core.util; import com.xxl.job.admin.core.conf.XxlJobAdminConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertiesLoaderUtils; import java.io.IOException; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * i18n util * * @author xuxueli 2018-01-17 20:39:06 */ public class I18nUtil { private static Logger logger = LoggerFactory.getLogger(I18nUtil.class); private static Properties prop = null; public static Properties loadI18nProp() { if (prop != null) { return prop; } try { // build i18n prop String i18n = XxlJobAdminConfig.getAdminConfig().getI18n(); String i18nFile = MessageFormat.format("i18n/message_{0}.properties", i18n); // load prop Resource resource = new ClassPathResource(i18nFile); EncodedResource encodedResource = new EncodedResource(resource, "UTF-8"); prop = PropertiesLoaderUtils.loadProperties(encodedResource); } catch (IOException e) { logger.error(e.getMessage(), e); } return prop; } /** * get val of i18n key * * @param key * @return */ public static String getString(String key) { return loadI18nProp().getProperty(key); } /** * get mult val of i18n mult key, as json * * @param keys * @return */ public static String getMultString(String... keys) { Map map = new HashMap(); Properties prop = loadI18nProp(); if (keys != null && keys.length > 0) { for (String key : keys) { map.put(key, prop.getProperty(key)); } } else { for (String key : prop.stringPropertyNames()) { map.put(key, prop.getProperty(key)); } } String json = JacksonUtil.writeValueAsString(map); return json; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/JacksonUtil.java ================================================ package com.xxl.job.admin.core.util; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; /** * Jackson util *

* 1、obj need private and set/get; * 2、do not support inner class; * * @author xuxueli 2015-9-25 18:02:56 */ public class JacksonUtil { private static Logger logger = LoggerFactory.getLogger(JacksonUtil.class); private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static ObjectMapper getInstance() { return OBJECT_MAPPER; } /** * bean、array、List、Map --> json * * @param obj * @return json string * @throws Exception */ public static String writeValueAsString(Object obj) { try { return getInstance().writeValueAsString(obj); } catch (JsonGenerationException e) { logger.error(e.getMessage(), e); } catch (JsonMappingException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } return null; } /** * string --> bean、Map、List(array) * * @param jsonStr * @param clazz * @return obj * @throws Exception */ public static T readValue(String jsonStr, Class clazz) { try { return getInstance().readValue(jsonStr, clazz); } catch (JsonParseException e) { logger.error(e.getMessage(), e); } catch (JsonMappingException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } return null; } /** * string --> List... * * @param jsonStr * @param parametrized * @param parameterClasses * @param * @return */ public static T readValue(String jsonStr, Class parametrized, Class... parameterClasses) { try { JavaType javaType = getInstance().getTypeFactory().constructParametricType(parametrized, parameterClasses); return getInstance().readValue(jsonStr, javaType); } catch (JsonParseException e) { logger.error(e.getMessage(), e); } catch (JsonMappingException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } return null; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java ================================================ package com.xxl.job.admin.core.util; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * local cache tool * * @author xuxueli 2018-01-22 21:37:34 */ public class LocalCacheUtil { private static ConcurrentMap cacheRepository = new ConcurrentHashMap(); // 类型建议用抽象父类,兼容性更好; private static class LocalCacheData { private String key; private Object val; private long timeoutTime; public LocalCacheData() { } public LocalCacheData(String key, Object val, long timeoutTime) { this.key = key; this.val = val; this.timeoutTime = timeoutTime; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Object getVal() { return val; } public void setVal(Object val) { this.val = val; } public long getTimeoutTime() { return timeoutTime; } public void setTimeoutTime(long timeoutTime) { this.timeoutTime = timeoutTime; } } /** * set cache * * @param key * @param val * @param cacheTime * @return */ public static boolean set(String key, Object val, long cacheTime) { // clean timeout cache, before set new cache (avoid cache too much) cleanTimeoutCache(); // set new cache if (key == null || key.trim().length() == 0) { return false; } if (val == null) { remove(key); } if (cacheTime <= 0) { remove(key); } long timeoutTime = System.currentTimeMillis() + cacheTime; LocalCacheData localCacheData = new LocalCacheData(key, val, timeoutTime); cacheRepository.put(localCacheData.getKey(), localCacheData); return true; } /** * remove cache * * @param key * @return */ public static boolean remove(String key) { if (key == null || key.trim().length() == 0) { return false; } cacheRepository.remove(key); return true; } /** * get cache * * @param key * @return */ public static Object get(String key) { if (key == null || key.trim().length() == 0) { return null; } LocalCacheData localCacheData = cacheRepository.get(key); if (localCacheData != null && System.currentTimeMillis() < localCacheData.getTimeoutTime()) { return localCacheData.getVal(); } else { remove(key); return null; } } /** * clean timeout cache * * @return */ public static boolean cleanTimeoutCache() { if (!cacheRepository.keySet().isEmpty()) { for (String key : cacheRepository.keySet()) { LocalCacheData localCacheData = cacheRepository.get(key); if (localCacheData != null && System.currentTimeMillis() >= localCacheData.getTimeoutTime()) { cacheRepository.remove(key); } } } return true; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobGroupDao.java ================================================ package com.xxl.job.admin.dao; import com.xxl.job.admin.core.model.XxlJobGroup; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * Created by xuxueli on 16/9/30. */ @Mapper public interface XxlJobGroupDao { public List findAll(); public List findByAddressType(@Param("addressType") int addressType); public int save(XxlJobGroup xxlJobGroup); public int update(XxlJobGroup xxlJobGroup); public int remove(@Param("id") int id); public XxlJobGroup load(@Param("id") int id); public List pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("appname") String appname, @Param("title") String title); public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("appname") String appname, @Param("title") String title); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java ================================================ package com.xxl.job.admin.dao; import com.xxl.job.admin.core.model.XxlJobInfo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * job info * * @author xuxueli 2016-1-12 18:03:45 */ @Mapper public interface XxlJobInfoDao { public List pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("triggerStatus") int triggerStatus, @Param("jobDesc") String jobDesc, @Param("executorHandler") String executorHandler, @Param("author") String author); public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("triggerStatus") int triggerStatus, @Param("jobDesc") String jobDesc, @Param("executorHandler") String executorHandler, @Param("author") String author); public int save(XxlJobInfo info); public XxlJobInfo loadById(@Param("id") int id); public int update(XxlJobInfo xxlJobInfo); public int delete(@Param("id") long id); public List getJobsByGroup(@Param("jobGroup") int jobGroup); public int findAllCount(); public List scheduleJobQuery(@Param("maxNextTime") long maxNextTime, @Param("pagesize") int pagesize); public int scheduleUpdate(XxlJobInfo xxlJobInfo); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java ================================================ package com.xxl.job.admin.dao; import com.xxl.job.admin.core.model.XxlJobLog; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.Date; import java.util.List; import java.util.Map; /** * job log * * @author xuxueli 2016-1-12 18:03:06 */ @Mapper public interface XxlJobLogDao { // exist jobId not use jobGroup, not exist use jobGroup public List pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("jobId") int jobId, @Param("triggerTimeStart") Date triggerTimeStart, @Param("triggerTimeEnd") Date triggerTimeEnd, @Param("logStatus") int logStatus); public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("jobId") int jobId, @Param("triggerTimeStart") Date triggerTimeStart, @Param("triggerTimeEnd") Date triggerTimeEnd, @Param("logStatus") int logStatus); public XxlJobLog load(@Param("id") long id); public long save(XxlJobLog xxlJobLog); public int updateTriggerInfo(XxlJobLog xxlJobLog); public int updateHandleInfo(XxlJobLog xxlJobLog); public int delete(@Param("jobId") int jobId); public Map findLogReport(@Param("from") Date from, @Param("to") Date to); public List findClearLogIds(@Param("jobGroup") int jobGroup, @Param("jobId") int jobId, @Param("clearBeforeTime") Date clearBeforeTime, @Param("clearBeforeNum") int clearBeforeNum, @Param("pagesize") int pagesize); public int clearLog(@Param("logIds") List logIds); public List findFailJobLogIds(@Param("pagesize") int pagesize); public int updateAlarmStatus(@Param("logId") long logId, @Param("oldAlarmStatus") int oldAlarmStatus, @Param("newAlarmStatus") int newAlarmStatus); public List findLostJobIds(@Param("losedTime") Date losedTime); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogGlueDao.java ================================================ package com.xxl.job.admin.dao; import com.xxl.job.admin.core.model.XxlJobLogGlue; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * job log for glue * * @author xuxueli 2016-5-19 18:04:56 */ @Mapper public interface XxlJobLogGlueDao { public int save(XxlJobLogGlue xxlJobLogGlue); public List findByJobId(@Param("jobId") int jobId); public int removeOld(@Param("jobId") int jobId, @Param("limit") int limit); public int deleteByJobId(@Param("jobId") int jobId); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogReportDao.java ================================================ package com.xxl.job.admin.dao; import com.xxl.job.admin.core.model.XxlJobLogReport; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.Date; import java.util.List; /** * job log * * @author xuxueli 2019-11-22 */ @Mapper public interface XxlJobLogReportDao { public int save(XxlJobLogReport xxlJobLogReport); public int update(XxlJobLogReport xxlJobLogReport); public List queryLogReport(@Param("triggerDayFrom") Date triggerDayFrom, @Param("triggerDayTo") Date triggerDayTo); public XxlJobLogReport queryLogReportTotal(); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobRegistryDao.java ================================================ package com.xxl.job.admin.dao; import com.xxl.job.admin.core.model.XxlJobRegistry; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.Date; import java.util.List; /** * Created by xuxueli on 16/9/30. */ @Mapper public interface XxlJobRegistryDao { public List findDead(@Param("timeout") int timeout, @Param("nowTime") Date nowTime); public int removeDead(@Param("ids") List ids); public List findAll(@Param("timeout") int timeout, @Param("nowTime") Date nowTime); public int registryUpdate(@Param("registryGroup") String registryGroup, @Param("registryKey") String registryKey, @Param("registryValue") String registryValue, @Param("updateTime") Date updateTime); public int registrySave(@Param("registryGroup") String registryGroup, @Param("registryKey") String registryKey, @Param("registryValue") String registryValue, @Param("updateTime") Date updateTime); public int registryDelete(@Param("registryGroup") String registryGroup, @Param("registryKey") String registryKey, @Param("registryValue") String registryValue); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobUserDao.java ================================================ package com.xxl.job.admin.dao; import com.xxl.job.admin.core.model.XxlJobUser; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @author xuxueli 2019-05-04 16:44:59 */ @Mapper public interface XxlJobUserDao { public List pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("username") String username, @Param("role") int role); public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("username") String username, @Param("role") int role); public XxlJobUser loadByUserName(@Param("username") String username); public int save(XxlJobUser xxlJobUser); public int update(XxlJobUser xxlJobUser); public int delete(@Param("id") int id); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/LoginService.java ================================================ package com.xxl.job.admin.service; import com.xxl.job.admin.core.model.XxlJobUser; import com.xxl.job.admin.core.util.CookieUtil; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.core.util.JacksonUtil; import com.xxl.job.admin.dao.XxlJobUserDao; import com.xxl.job.core.biz.model.ReturnT; import org.springframework.context.annotation.Configuration; import org.springframework.util.DigestUtils; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.math.BigInteger; /** * @author xuxueli 2019-05-04 22:13:264 */ @Configuration public class LoginService { public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY"; @Resource private XxlJobUserDao xxlJobUserDao; private String makeToken(XxlJobUser xxlJobUser) { String tokenJson = JacksonUtil.writeValueAsString(xxlJobUser); String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16); return tokenHex; } private XxlJobUser parseToken(String tokenHex) { XxlJobUser xxlJobUser = null; if (tokenHex != null) { String tokenJson = new String(new BigInteger(tokenHex, 16).toByteArray()); // username_password(md5) xxlJobUser = JacksonUtil.readValue(tokenJson, XxlJobUser.class); } return xxlJobUser; } public ReturnT login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember) { // param if (username == null || username.trim().length() == 0 || password == null || password.trim().length() == 0) { return new ReturnT(500, I18nUtil.getString("login_param_empty")); } // valid passowrd XxlJobUser xxlJobUser = xxlJobUserDao.loadByUserName(username); if (xxlJobUser == null) { return new ReturnT(500, I18nUtil.getString("login_param_unvalid")); } String passwordMd5 = DigestUtils.md5DigestAsHex(password.getBytes()); if (!passwordMd5.equals(xxlJobUser.getPassword())) { return new ReturnT(500, I18nUtil.getString("login_param_unvalid")); } String loginToken = makeToken(xxlJobUser); // do login CookieUtil.set(response, LOGIN_IDENTITY_KEY, loginToken, ifRemember); return ReturnT.SUCCESS; } /** * logout * * @param request * @param response */ public ReturnT logout(HttpServletRequest request, HttpServletResponse response) { CookieUtil.remove(request, response, LOGIN_IDENTITY_KEY); return ReturnT.SUCCESS; } /** * logout * * @param request * @return */ public XxlJobUser ifLogin(HttpServletRequest request, HttpServletResponse response) { String cookieToken = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY); if (cookieToken != null) { XxlJobUser cookieUser = null; try { cookieUser = parseToken(cookieToken); } catch (Exception e) { logout(request, response); } if (cookieUser != null) { XxlJobUser dbUser = xxlJobUserDao.loadByUserName(cookieUser.getUsername()); if (dbUser != null) { if (cookieUser.getPassword().equals(dbUser.getPassword())) { return dbUser; } } } } return null; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java ================================================ package com.xxl.job.admin.service; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.core.biz.model.ReturnT; import java.util.Date; import java.util.Map; /** * core job action for xxl-job * * @author xuxueli 2016-5-28 15:30:33 */ public interface XxlJobService { /** * page list * * @param start * @param length * @param jobGroup * @param jobDesc * @param executorHandler * @param author * @return */ public Map pageList(int start, int length, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author); /** * add job * * @param jobInfo * @return */ public ReturnT add(XxlJobInfo jobInfo); /** * update job * * @param jobInfo * @return */ public ReturnT update(XxlJobInfo jobInfo); /** * remove job * * * * @param id * @return */ public ReturnT remove(int id); /** * start job * * @param id * @return */ public ReturnT start(int id); /** * stop job * * @param id * @return */ public ReturnT stop(int id); /** * dashboard info * * @return */ public Map dashboardInfo(); /** * chart info * * @param startDate * @param endDate * @return */ public ReturnT> chartInfo(Date startDate, Date endDate); } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java ================================================ package com.xxl.job.admin.service.impl; import com.xxl.job.admin.core.thread.JobCompleteHelper; import com.xxl.job.admin.core.thread.JobRegistryHelper; import com.xxl.job.core.biz.AdminBiz; import com.xxl.job.core.biz.model.HandleCallbackParam; import com.xxl.job.core.biz.model.RegistryParam; import com.xxl.job.core.biz.model.ReturnT; import org.springframework.stereotype.Service; import java.util.List; /** * @author xuxueli 2017-07-27 21:54:20 */ @Service public class AdminBizImpl implements AdminBiz { @Override public ReturnT callback(List callbackParamList) { return JobCompleteHelper.getInstance().callback(callbackParamList); } @Override public ReturnT registry(RegistryParam registryParam) { return JobRegistryHelper.getInstance().registry(registryParam); } @Override public ReturnT registryRemove(RegistryParam registryParam) { return JobRegistryHelper.getInstance().registryRemove(registryParam); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java ================================================ package com.xxl.job.admin.service.impl; import com.xxl.job.admin.core.cron.CronExpression; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLogReport; import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.core.thread.JobScheduleHelper; import com.xxl.job.admin.core.util.I18nUtil; import com.xxl.job.admin.dao.*; import com.xxl.job.admin.service.XxlJobService; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.util.DateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.text.MessageFormat; import java.util.*; /** * core job action for xxl-job * * @author xuxueli 2016-5-28 15:30:33 */ @Service public class XxlJobServiceImpl implements XxlJobService { private static Logger logger = LoggerFactory.getLogger(XxlJobServiceImpl.class); @Resource private XxlJobGroupDao xxlJobGroupDao; @Resource private XxlJobInfoDao xxlJobInfoDao; @Resource public XxlJobLogDao xxlJobLogDao; @Resource private XxlJobLogGlueDao xxlJobLogGlueDao; @Resource private XxlJobLogReportDao xxlJobLogReportDao; @Override public Map pageList(int start, int length, int jobGroup, int triggerStatus, String jobDesc, String executorHandler, String author) { // page list List list = xxlJobInfoDao.pageList(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author); int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, triggerStatus, jobDesc, executorHandler, author); // package result Map maps = new HashMap(); maps.put("recordsTotal", list_count); // 总记录数 maps.put("recordsFiltered", list_count); // 过滤后的总记录数 maps.put("data", list); // 分页列表 return maps; } @Override public ReturnT add(XxlJobInfo jobInfo) { // valid base XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup()); if (group == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_choose") + I18nUtil.getString("jobinfo_field_jobgroup"))); } if (jobInfo.getJobDesc() == null || jobInfo.getJobDesc().trim().length() == 0) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_jobdesc"))); } if (jobInfo.getAuthor() == null || jobInfo.getAuthor().trim().length() == 0) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_author"))); } // valid trigger ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null); if (scheduleTypeEnum == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } if (scheduleTypeEnum == ScheduleTypeEnum.CRON) { if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) { return new ReturnT(ReturnT.FAIL_CODE, "Cron" + I18nUtil.getString("system_unvalid")); } } else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE/* || scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) { if (jobInfo.getScheduleConf() == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type"))); } try { int fixSecond = Integer.valueOf(jobInfo.getScheduleConf()); if (fixSecond < 1) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } } catch (Exception e) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } } // valid job if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype") + I18nUtil.getString("system_unvalid"))); } if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType()) && (jobInfo.getExecutorHandler() == null || jobInfo.getExecutorHandler().trim().length() == 0)) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input") + "JobHandler")); } // 》fix "\r" in shell if (GlueTypeEnum.GLUE_SHELL == GlueTypeEnum.match(jobInfo.getGlueType()) && jobInfo.getGlueSource() != null) { jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", "")); } // valid advanced if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil.getString("system_unvalid"))); } if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("misfire_strategy") + I18nUtil.getString("system_unvalid"))); } if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil.getString("system_unvalid"))); } // 》ChildJobId valid if (jobInfo.getChildJobId() != null && jobInfo.getChildJobId().trim().length() > 0) { String[] childJobIds = jobInfo.getChildJobId().split(","); for (String childJobIdItem : childJobIds) { if (childJobIdItem != null && childJobIdItem.trim().length() > 0 && isNumeric(childJobIdItem)) { XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.parseInt(childJobIdItem)); if (childJobInfo == null) { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_not_found")), childJobIdItem)); } } else { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_unvalid")), childJobIdItem)); } } // join , avoid "xxx,," String temp = ""; for (String item : childJobIds) { temp += item + ","; } temp = temp.substring(0, temp.length() - 1); jobInfo.setChildJobId(temp); } // add in db jobInfo.setAddTime(new Date()); jobInfo.setUpdateTime(new Date()); jobInfo.setGlueUpdatetime(new Date()); xxlJobInfoDao.save(jobInfo); if (jobInfo.getId() < 1) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_add") + I18nUtil.getString("system_fail"))); } return new ReturnT(String.valueOf(jobInfo.getId())); } private boolean isNumeric(String str) { try { int result = Integer.valueOf(str); return true; } catch (NumberFormatException e) { return false; } } @Override public ReturnT update(XxlJobInfo jobInfo) { // valid base if (jobInfo.getJobDesc() == null || jobInfo.getJobDesc().trim().length() == 0) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_jobdesc"))); } if (jobInfo.getAuthor() == null || jobInfo.getAuthor().trim().length() == 0) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_field_author"))); } // valid trigger ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null); if (scheduleTypeEnum == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } if (scheduleTypeEnum == ScheduleTypeEnum.CRON) { if (jobInfo.getScheduleConf() == null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) { return new ReturnT(ReturnT.FAIL_CODE, "Cron" + I18nUtil.getString("system_unvalid")); } } else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE /*|| scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) { if (jobInfo.getScheduleConf() == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } try { int fixSecond = Integer.valueOf(jobInfo.getScheduleConf()); if (fixSecond < 1) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } } catch (Exception e) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } } // valid advanced if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil.getString("system_unvalid"))); } if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("misfire_strategy") + I18nUtil.getString("system_unvalid"))); } if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil.getString("system_unvalid"))); } // 》ChildJobId valid if (jobInfo.getChildJobId() != null && jobInfo.getChildJobId().trim().length() > 0) { String[] childJobIds = jobInfo.getChildJobId().split(","); for (String childJobIdItem : childJobIds) { if (childJobIdItem != null && childJobIdItem.trim().length() > 0 && isNumeric(childJobIdItem)) { XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.parseInt(childJobIdItem)); if (childJobInfo == null) { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_not_found")), childJobIdItem)); } } else { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId") + "({0})" + I18nUtil.getString("system_unvalid")), childJobIdItem)); } } // join , avoid "xxx,," String temp = ""; for (String item : childJobIds) { temp += item + ","; } temp = temp.substring(0, temp.length() - 1); jobInfo.setChildJobId(temp); } // group valid XxlJobGroup jobGroup = xxlJobGroupDao.load(jobInfo.getJobGroup()); if (jobGroup == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_jobgroup") + I18nUtil.getString("system_unvalid"))); } // stage job info XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(jobInfo.getId()); if (exists_jobInfo == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_not_found"))); } // next trigger time (5s后生效,避开预读周期) long nextTriggerTime = exists_jobInfo.getTriggerNextTime(); boolean scheduleDataNotChanged = jobInfo.getScheduleType().equals(exists_jobInfo.getScheduleType()) && jobInfo.getScheduleConf().equals(exists_jobInfo.getScheduleConf()); if (exists_jobInfo.getTriggerStatus() == 1 && !scheduleDataNotChanged) { try { Date nextValidTime = JobScheduleHelper.generateNextValidTime(jobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS)); if (nextValidTime == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } nextTriggerTime = nextValidTime.getTime(); } catch (Exception e) { logger.error(e.getMessage(), e); return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } } exists_jobInfo.setJobGroup(jobInfo.getJobGroup()); exists_jobInfo.setJobDesc(jobInfo.getJobDesc()); exists_jobInfo.setAuthor(jobInfo.getAuthor()); exists_jobInfo.setAlarmEmail(jobInfo.getAlarmEmail()); exists_jobInfo.setScheduleType(jobInfo.getScheduleType()); exists_jobInfo.setScheduleConf(jobInfo.getScheduleConf()); exists_jobInfo.setMisfireStrategy(jobInfo.getMisfireStrategy()); exists_jobInfo.setExecutorRouteStrategy(jobInfo.getExecutorRouteStrategy()); exists_jobInfo.setExecutorHandler(jobInfo.getExecutorHandler()); exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam()); exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy()); exists_jobInfo.setExecutorTimeout(jobInfo.getExecutorTimeout()); exists_jobInfo.setExecutorFailRetryCount(jobInfo.getExecutorFailRetryCount()); exists_jobInfo.setChildJobId(jobInfo.getChildJobId()); exists_jobInfo.setTriggerNextTime(nextTriggerTime); exists_jobInfo.setUpdateTime(new Date()); xxlJobInfoDao.update(exists_jobInfo); return ReturnT.SUCCESS; } @Override public ReturnT remove(int id) { XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id); if (xxlJobInfo == null) { return ReturnT.SUCCESS; } xxlJobInfoDao.delete(id); xxlJobLogDao.delete(id); xxlJobLogGlueDao.deleteByJobId(id); return ReturnT.SUCCESS; } @Override public ReturnT start(int id) { XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id); // valid ScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(xxlJobInfo.getScheduleType(), ScheduleTypeEnum.NONE); if (ScheduleTypeEnum.NONE == scheduleTypeEnum) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type_none_limit_start"))); } // next trigger time (5s后生效,避开预读周期) long nextTriggerTime = 0; try { Date nextValidTime = JobScheduleHelper.generateNextValidTime(xxlJobInfo, new Date(System.currentTimeMillis() + JobScheduleHelper.PRE_READ_MS)); if (nextValidTime == null) { return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } nextTriggerTime = nextValidTime.getTime(); } catch (Exception e) { logger.error(e.getMessage(), e); return new ReturnT(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type") + I18nUtil.getString("system_unvalid"))); } xxlJobInfo.setTriggerStatus(1); xxlJobInfo.setTriggerLastTime(0); xxlJobInfo.setTriggerNextTime(nextTriggerTime); xxlJobInfo.setUpdateTime(new Date()); xxlJobInfoDao.update(xxlJobInfo); return ReturnT.SUCCESS; } @Override public ReturnT stop(int id) { XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id); xxlJobInfo.setTriggerStatus(0); xxlJobInfo.setTriggerLastTime(0); xxlJobInfo.setTriggerNextTime(0); xxlJobInfo.setUpdateTime(new Date()); xxlJobInfoDao.update(xxlJobInfo); return ReturnT.SUCCESS; } @Override public Map dashboardInfo() { int jobInfoCount = xxlJobInfoDao.findAllCount(); int jobLogCount = 0; int jobLogSuccessCount = 0; XxlJobLogReport xxlJobLogReport = xxlJobLogReportDao.queryLogReportTotal(); if (xxlJobLogReport != null) { jobLogCount = xxlJobLogReport.getRunningCount() + xxlJobLogReport.getSucCount() + xxlJobLogReport.getFailCount(); jobLogSuccessCount = xxlJobLogReport.getSucCount(); } // executor count Set executorAddressSet = new HashSet(); List groupList = xxlJobGroupDao.findAll(); if (groupList != null && !groupList.isEmpty()) { for (XxlJobGroup group : groupList) { if (group.getRegistryList() != null && !group.getRegistryList().isEmpty()) { executorAddressSet.addAll(group.getRegistryList()); } } } int executorCount = executorAddressSet.size(); Map dashboardMap = new HashMap(); dashboardMap.put("jobInfoCount", jobInfoCount); dashboardMap.put("jobLogCount", jobLogCount); dashboardMap.put("jobLogSuccessCount", jobLogSuccessCount); dashboardMap.put("executorCount", executorCount); return dashboardMap; } @Override public ReturnT> chartInfo(Date startDate, Date endDate) { // process List triggerDayList = new ArrayList(); List triggerDayCountRunningList = new ArrayList(); List triggerDayCountSucList = new ArrayList(); List triggerDayCountFailList = new ArrayList(); int triggerCountRunningTotal = 0; int triggerCountSucTotal = 0; int triggerCountFailTotal = 0; List logReportList = xxlJobLogReportDao.queryLogReport(startDate, endDate); if (logReportList != null && logReportList.size() > 0) { for (XxlJobLogReport item : logReportList) { String day = DateUtil.formatDate(item.getTriggerDay()); int triggerDayCountRunning = item.getRunningCount(); int triggerDayCountSuc = item.getSucCount(); int triggerDayCountFail = item.getFailCount(); triggerDayList.add(day); triggerDayCountRunningList.add(triggerDayCountRunning); triggerDayCountSucList.add(triggerDayCountSuc); triggerDayCountFailList.add(triggerDayCountFail); triggerCountRunningTotal += triggerDayCountRunning; triggerCountSucTotal += triggerDayCountSuc; triggerCountFailTotal += triggerDayCountFail; } } else { for (int i = -6; i <= 0; i++) { triggerDayList.add(DateUtil.formatDate(DateUtil.addDays(new Date(), i))); triggerDayCountRunningList.add(0); triggerDayCountSucList.add(0); triggerDayCountFailList.add(0); } } Map result = new HashMap(); result.put("triggerDayList", triggerDayList); result.put("triggerDayCountRunningList", triggerDayCountRunningList); result.put("triggerDayCountSucList", triggerDayCountSucList); result.put("triggerDayCountFailList", triggerDayCountFailList); result.put("triggerCountRunningTotal", triggerCountRunningTotal); result.put("triggerCountSucTotal", triggerCountSucTotal); result.put("triggerCountFailTotal", triggerCountFailTotal); return new ReturnT>(result); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml ================================================ --- # 监控配置 spring.boot.admin.client: # 增加客户端开关 enabled: true # 设置 Spring Boot Admin Server 地址 url: http://localhost:9090/admin instance: service-host-type: IP username: ruoyi password: 123456 --- # 数据库配置 spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://81.68.131.40:3306/paizhicheng?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai username: Paizhicheng password: pZdDw5YKLKbWFfEB hikari: auto-commit: true connection-test-query: SELECT 1 connection-timeout: 10000 idle-timeout: 30000 max-lifetime: 900000 maximum-pool-size: 30 minimum-idle: 10 pool-name: HikariCP validation-timeout: 1000 --- # 邮件配置 spring: mail: from: xxx@qq.com host: smtp.qq.com username: xxx@qq.com password: xxx port: 25 properties: mail: smtp: auth: true socketFactory: class: javax.net.ssl.SSLSocketFactory starttls: enable: true required: true ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-prod.yml ================================================ --- # 监控配置 spring.boot.admin.client: # 增加客户端开关 enabled: true # 设置 Spring Boot Admin Server 地址 url: http://localhost:9090/admin instance: service-host-type: IP username: ruoyi password: 123456 --- # 数据库配置 spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai username: root password: root hikari: auto-commit: true connection-test-query: SELECT 1 connection-timeout: 10000 idle-timeout: 30000 max-lifetime: 900000 maximum-pool-size: 30 minimum-idle: 10 pool-name: HikariCP validation-timeout: 1000 --- # 邮件配置 spring: mail: from: xxx@qq.com host: smtp.qq.com username: xxx@qq.com password: xxx port: 25 properties: mail: smtp: auth: true socketFactory: class: javax.net.ssl.SSLSocketFactory starttls: enable: true required: true ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml ================================================ --- # server 配置 server: port: 9100 servlet: context-path: /xxl-job-admin spring: application: name: ruoyi-xxl-job-admin profiles: active: @profiles.active@ mvc: servlet: load-on-startup: 0 static-path-pattern: /static/** web: resources: static-locations: classpath:/static/ logging: config: classpath:logback-plus.xml --- # mybatis 配置 mybatis: mapper-locations: classpath:/mybatis-mapper/*Mapper.xml --- # 页面配置 spring: freemarker: charset: UTF-8 request-context-attribute: request settings: number_format: 0.########## suffix: .ftl templateLoaderPath: classpath:/templates/ --- # Actuator 监控端点的配置项 management: health: mail: enabled: false endpoints: web: exposure: include: '*' endpoint: health: show-details: ALWAYS logfile: external-file: ./logs/ruoyi-xxl-job-admin.log --- # xxljob系统配置 xxl: job: # 鉴权token accessToken: xxl-job # 国际化 i18n: zh_CN # 日志清理 logretentiondays: 30 triggerpool: fast: max: 200 slow: max: 100 ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/banner.txt ================================================ Application Version: ${ruoyi-vue-plus.version} Spring Boot Version: ${spring-boot.version} __ __ _ _ _ _ _ \ \ / / | | | | | | /\ | | (_) \ V / __ _| |______ | | ___ | |__ ______ / \ __| |_ __ ___ _ _ __ > < \ \/ / |______| | |/ _ \| '_ \______/ /\ \ / _` | '_ ` _ \| | '_ \ / . \ > <| | | |__| | (_) | |_) | / ____ \ (_| | | | | | | | | | | /_/ \_\/_/\_\_| \____/ \___/|_.__/ /_/ \_\__,_|_| |_| |_|_|_| |_| ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties ================================================ admin_name=Scheduling Center admin_name_full=Distributed Task Scheduling Platform XXL-JOB admin_version=2.4.0 admin_i18n=en ## system system_tips=System message system_ok=Confirm system_close=Close system_save=Save system_cancel=Cancel system_search=Search system_status=Status system_opt=Operate system_please_input=please input system_please_choose=please choose system_success=success system_fail=fail system_add_suc=add success system_add_fail=add fail system_update_suc=update success system_update_fail=update fail system_all=All system_api_error=net error system_show=Show system_empty=Empty system_opt_suc=operate success system_opt_fail=operate fail system_opt_edit=Edit system_opt_del=Delete system_opt_copy=Copy system_unvalid=illegal system_not_found=not exist system_nav=Navigation system_digits=digits system_lengh_limit=Length limit system_permission_limit=Permission limit system_welcome=Welcome ## daterangepicker daterangepicker_ranges_recent_hour=recent one hour daterangepicker_ranges_today=today daterangepicker_ranges_yesterday=yesterday daterangepicker_ranges_this_month=this month daterangepicker_ranges_last_month=last month daterangepicker_ranges_recent_week=recent one week daterangepicker_ranges_recent_month=recent one month daterangepicker_custom_name=custom daterangepicker_custom_starttime=start time daterangepicker_custom_endtime=end time daterangepicker_custom_daysofweek=Sun,Mon,Tue,Wed,Thu,Fri,Sat daterangepicker_custom_monthnames=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec ## dataTable dataTable_sProcessing=processing... dataTable_sLengthMenu= _MENU_ records per page dataTable_sZeroRecords=No matching results dataTable_sInfo=page _PAGE_ ( Total _PAGES_ pages,_TOTAL_ records ) dataTable_sInfoEmpty=No Record dataTable_sInfoFiltered=(Filtered by _MAX_ results) dataTable_sSearch=Search dataTable_sEmptyTable=Table data is empty dataTable_sLoadingRecords=Loading... dataTable_sFirst=FIRST PAGE dataTable_sPrevious=Previous Page dataTable_sNext=Next Page dataTable_sLast=LAST PAGE dataTable_sSortAscending=: Rank this column in ascending order dataTable_sSortDescending=: Rank this column in descending order ## login login_btn=Login login_remember_me=Remember Me login_username_placeholder=Please enter username login_password_placeholder=Please enter password login_username_empty=Please enter username login_username_lt_4=Username length should not be less than 4 login_password_empty=Please enter password login_password_lt_4=Password length should not be less than 4 login_success=Login success login_fail=Login fail login_param_empty=Username or password is empty login_param_unvalid=Username or password error ## logout logout_btn=Logout logout_confirm=Confirm logout? logout_success=Logout success logout_fail=Logout fail ## change pwd change_pwd=Change password change_pwd_suc_to_logout=Change password successful, about to log out login change_pwd_field_newpwd=new password ## dashboard job_dashboard_name=Run report job_dashboard_job_num=Job number job_dashboard_job_num_tip=The number of tasks running in the scheduling center job_dashboard_trigger_num=trigger number job_dashboard_trigger_num_tip=The number of trigger record scheduled by the scheduling center job_dashboard_jobgroup_num=Executor number job_dashboard_jobgroup_num_tip=The number of online executor machines perceived by the scheduling center job_dashboard_report=Scheduling report job_dashboard_report_loaddata_fail=Scheduling report load data error job_dashboard_date_report=Date distribution job_dashboard_rate_report=Percentage distribution ## job info jobinfo_name=Job Manage jobinfo_job=Job jobinfo_field_add=Add Job jobinfo_field_update=Edit Job jobinfo_field_id=Job ID jobinfo_field_jobgroup=Executor jobinfo_field_jobdesc=Job description jobinfo_field_timeout=Job timeout period jobinfo_field_gluetype=GLUE Type jobinfo_field_executorparam=Param jobinfo_field_author=Author jobinfo_field_alarmemail=Alarm email jobinfo_field_alarmemail_placeholder=Please enter alarm mail, if there are more than one comma separated jobinfo_field_executorRouteStrategy=Route Strategy jobinfo_field_childJobId=Child Job ID jobinfo_field_childJobId_placeholder=Please enter the Child job ID, if there are more than one comma separated jobinfo_field_executorBlockStrategy=Block Strategy jobinfo_field_executorFailRetryCount=Fail Retry Count jobinfo_field_executorFailRetryCount_placeholder=Fail Retry Count. effect if greater than zero jobinfo_script_location=Script location jobinfo_shard_index=Shard index jobinfo_shard_total=Shard total jobinfo_opt_stop=Stop jobinfo_opt_start=Start jobinfo_opt_log=Query Log jobinfo_opt_run=Run Once jobinfo_opt_run_tips=Please input the address for this trigger. Null will be obtained from the executor jobinfo_opt_registryinfo=Registry Info jobinfo_opt_next_time=Next trigger time jobinfo_glue_remark=Resource Remark jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100 jobinfo_glue_rollback=Version Backtrack jobinfo_glue_jobid_unvalid=Job ID is illegal jobinfo_glue_gluetype_unvalid=The job is not GLUE Type jobinfo_field_executorTimeout_placeholder=Job Timeout period,in seconds. effect if greater than zero schedule_type=Schedule Type schedule_type_none=None schedule_type_cron=Cron schedule_type_fix_rate=Fix rate schedule_type_fix_delay=Fix delay schedule_type_none_limit_start=The current schedule type disables startup misfire_strategy=Misfire strategy misfire_strategy_do_nothing=Do nothing misfire_strategy_fire_once_now=Fire once now jobinfo_conf_base=Base configuration jobinfo_conf_schedule=Schedule configuration jobinfo_conf_job=Job configuration jobinfo_conf_advanced=Advanced configuration ## job log joblog_name=Trigger Log joblog_status=Status joblog_status_all=All joblog_status_suc=Success joblog_status_fail=Fail joblog_status_running=Running joblog_field_triggerTime=Trigger Time joblog_field_triggerCode=Trigger Result joblog_field_triggerMsg=Trigger Msg joblog_field_handleTime=Handle Time joblog_field_handleCode=Handle Result joblog_field_handleMsg=Trigger Msg joblog_field_executorAddress=Executor Address joblog_clean=Clean joblog_clean_log=Clean Log joblog_clean_type=Clean Type joblog_clean_type_1=Clean up log data a month ago joblog_clean_type_2=Clean up log data three month ago joblog_clean_type_3=Clean up log data six month ago joblog_clean_type_4=Clean up log data a year ago joblog_clean_type_5=Clean up log data a thousand record ago joblog_clean_type_6=Clean up log data ten thousand record ago joblog_clean_type_7=Clean up log data thirty thousand record ago joblog_clean_type_8=Clean up log data hundred thousand record ago joblog_clean_type_9=Clean up all log data joblog_clean_type_unvalid=Clean type is illegal joblog_handleCode_200=Success joblog_handleCode_500=Fail joblog_handleCode_502=Timeout joblog_kill_log=Kill Job joblog_kill_log_limit=Trigger Fail, can not kill job joblog_kill_log_byman=Manual operation, kill job joblog_lost_fail=Job result lost, marked as failure joblog_rolling_log=Rolling log joblog_rolling_log_refresh=Refresh joblog_rolling_log_triggerfail=The job trigger fail, can not view the rolling log joblog_rolling_log_failoften=The request for the Rolling log is terminated, the number of failed requests exceeds the limit, Reload the log on the refresh page joblog_logid_unvalid=Log ID is illegal ## job group jobgroup_name=Executor Manage jobgroup_list=Executor List jobgroup_add=Add Executor jobgroup_edit=Edit Executor jobgroup_del=Delete Executor jobgroup_field_title=Title jobgroup_field_addressType=Registry Type jobgroup_field_addressType_0=Automatic registration jobgroup_field_addressType_1=Manual registration jobgroup_field_addressType_limit=Manually registration type, the machine address must not be empty jobgroup_field_registryList=machine address jobgroup_field_registryList_unvalid=registry machine address is illegal jobgroup_field_registryList_placeholder=Please enter the machine address, if there are more than one comma separated jobgroup_field_appname_limit=Limit the beginning of a lowercase letter, consists of lowercase letters、number and hyphen. jobgroup_field_appname_length=AppName length is limited to 4~64 jobgroup_field_title_length=Title length is limited to 4~12 jobgroup_field_order_digits=Please enter a positive integer jobgroup_field_orderrange=Order is limited to 1~1000 jobgroup_del_limit_0=Refuse to delete, the executor is being used jobgroup_del_limit_1=Refuses to delete, the system retains at least one executor jobgroup_empty=There is no valid executor. Please contact the administrator ## job conf jobconf_block_SERIAL_EXECUTION=Serial execution jobconf_block_DISCARD_LATER=Discard Later jobconf_block_COVER_EARLY=Cover Early jobconf_route_first=First jobconf_route_last=Last jobconf_route_round=Round jobconf_route_random=Random jobconf_route_consistenthash=Consistent Hash jobconf_route_lfu=Least Frequently Used jobconf_route_lru=Least Recently Used jobconf_route_failover=Failover jobconf_route_busyover=Busyover jobconf_route_shard=Sharding Broadcast jobconf_idleBeat=Idle check jobconf_beat=Heartbeats jobconf_monitor=Task Scheduling Center monitor alarm jobconf_monitor_detail=monitor alarm details jobconf_monitor_alarm_title=Alarm Type jobconf_monitor_alarm_type=Trigger Fail jobconf_monitor_alarm_content=Alarm Content jobconf_trigger_admin_adress=Trigger machine address jobconf_trigger_exe_regtype=Execotor-Registry Type jobconf_trigger_exe_regaddress=Execotor-Registry Address jobconf_trigger_address_empty=Trigger Fail:registry address is empty jobconf_trigger_run=Trigger Job jobconf_trigger_child_run=Trigger child job jobconf_callback_child_msg1={0}/{1} [Job ID={2}], Trigger {3}, Trigger msg: {4}
jobconf_callback_child_msg2={0}/{1} [Job ID={2}], Trigger Fail, Trigger msg: Job ID is illegal
jobconf_trigger_type=Job trigger type jobconf_trigger_type_cron=Cron trigger jobconf_trigger_type_manual=Manual trigger jobconf_trigger_type_parent=Parent job trigger jobconf_trigger_type_api=Api trigger jobconf_trigger_type_retry=Fail retry trigger jobconf_trigger_type_misfire=Misfire compensation trigger ## user user_manage=User Manage user_username=Username user_password=Password user_role=Role user_role_admin=Admin User user_role_normal=Normal User user_permission=Permission user_add=Add User user_update=Edit User user_username_repeat=Username Repeat user_username_valid=Restrictions start with a lowercase letter and consist of lowercase letters and Numbers user_password_update_placeholder=Please input password, empty means not update user_update_loginuser_limit=Operation of current login account is not allowed ## help job_help=Tutorial job_help_document=Official Document ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties ================================================ admin_name=任务调度中心 admin_name_full=分布式任务调度平台XXL-JOB admin_version=2.4.0 admin_i18n= ## system system_tips=系统提示 system_ok=确定 system_close=关闭 system_save=保存 system_cancel=取消 system_search=搜索 system_status=状态 system_opt=操作 system_please_input=请输入 system_please_choose=请选择 system_success=成功 system_fail=失败 system_add_suc=新增成功 system_add_fail=新增失败 system_update_suc=更新成功 system_update_fail=更新失败 system_all=全部 system_api_error=接口异常 system_show=查看 system_empty=无 system_opt_suc=操作成功 system_opt_fail=操作失败 system_opt_edit=编辑 system_opt_del=删除 system_opt_copy=复制 system_unvalid=非法 system_not_found=不存在 system_nav=导航 system_digits=整数 system_lengh_limit=长度限制 system_permission_limit=权限拦截 system_welcome=欢迎 ## daterangepicker daterangepicker_ranges_recent_hour=最近一小时 daterangepicker_ranges_today=今日 daterangepicker_ranges_yesterday=昨日 daterangepicker_ranges_this_month=本月 daterangepicker_ranges_last_month=上个月 daterangepicker_ranges_recent_week=最近一周 daterangepicker_ranges_recent_month=最近一月 daterangepicker_custom_name=自定义 daterangepicker_custom_starttime=起始时间 daterangepicker_custom_endtime=结束时间 daterangepicker_custom_daysofweek=日,一,二,三,四,五,六 daterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月 ## dataTable dataTable_sProcessing=处理中... dataTable_sLengthMenu=每页 _MENU_ 条记录 dataTable_sZeroRecords=没有匹配结果 dataTable_sInfo=第 _PAGE_ 页 ( 总共 _PAGES_ 页,_TOTAL_ 条记录 ) dataTable_sInfoEmpty=无记录 dataTable_sInfoFiltered=(由 _MAX_ 项结果过滤) dataTable_sSearch=搜索 dataTable_sEmptyTable=表中数据为空 dataTable_sLoadingRecords=载入中... dataTable_sFirst=首页 dataTable_sPrevious=上页 dataTable_sNext=下页 dataTable_sLast=末页 dataTable_sSortAscending=: 以升序排列此列 dataTable_sSortDescending=: 以降序排列此列 ## login login_btn=登录 login_remember_me=记住密码 login_username_placeholder=请输入登录账号 login_password_placeholder=请输入登录密码 login_username_empty=请输入登录账号 login_username_lt_4=登录账号不应低于4位 login_password_empty=请输入登录密码 login_password_lt_4=登录密码不应低于4位 login_success=登录成功 login_fail=登录失败 login_param_empty=账号或密码为空 login_param_unvalid=账号或密码错误 ## logout logout_btn=注销 logout_confirm=确认注销登录? logout_success=注销成功 logout_fail=注销失败 ## change pwd change_pwd=修改密码 change_pwd_suc_to_logout=修改密码成功,即将注销登陆 change_pwd_field_newpwd=新密码 ## dashboard job_dashboard_name=运行报表 job_dashboard_job_num=任务数量 job_dashboard_job_num_tip=调度中心运行的任务数量 job_dashboard_trigger_num=调度次数 job_dashboard_trigger_num_tip=调度中心触发的调度次数 job_dashboard_jobgroup_num=执行器数量 job_dashboard_jobgroup_num_tip=调度中心在线的执行器机器数量 job_dashboard_report=调度报表 job_dashboard_report_loaddata_fail=调度报表数据加载异常 job_dashboard_date_report=日期分布图 job_dashboard_rate_report=成功比例图 ## job info jobinfo_name=任务管理 jobinfo_job=任务 jobinfo_field_add=新增 jobinfo_field_update=更新任务 jobinfo_field_id=任务ID jobinfo_field_jobgroup=执行器 jobinfo_field_jobdesc=任务描述 jobinfo_field_gluetype=运行模式 jobinfo_field_executorparam=任务参数 jobinfo_field_author=负责人 jobinfo_field_timeout=任务超时时间 jobinfo_field_alarmemail=报警邮件 jobinfo_field_alarmemail_placeholder=请输入报警邮件,多个邮件地址则逗号分隔 jobinfo_field_executorRouteStrategy=路由策略 jobinfo_field_childJobId=子任务ID jobinfo_field_childJobId_placeholder=请输入子任务的任务ID,如存在多个则逗号分隔 jobinfo_field_executorBlockStrategy=阻塞处理策略 jobinfo_field_executorFailRetryCount=失败重试次数 jobinfo_field_executorFailRetryCount_placeholder=失败重试次数,大于零时生效 jobinfo_script_location=脚本位置 jobinfo_shard_index=分片序号 jobinfo_shard_total=分片总数 jobinfo_opt_stop=停止 jobinfo_opt_start=启动 jobinfo_opt_log=查询日志 jobinfo_opt_run=执行一次 jobinfo_opt_run_tips=请输入本次执行的机器地址,为空则从执行器获取 jobinfo_opt_registryinfo=注册节点 jobinfo_opt_next_time=下次执行时间 jobinfo_glue_remark=源码备注 jobinfo_glue_remark_limit=源码备注长度限制为4~100 jobinfo_glue_rollback=版本回溯 jobinfo_glue_jobid_unvalid=任务ID非法 jobinfo_glue_gluetype_unvalid=该任务非GLUE模式 jobinfo_field_executorTimeout_placeholder=任务超时时间,单位秒,大于零时生效 schedule_type=调度类型 schedule_type_none=无 schedule_type_cron=CRON schedule_type_fix_rate=固定速度 schedule_type_fix_delay=固定延迟 schedule_type_none_limit_start=当前调度类型禁止启动 misfire_strategy=调度过期策略 misfire_strategy_do_nothing=忽略 misfire_strategy_fire_once_now=立即执行一次 jobinfo_conf_base=基础配置 jobinfo_conf_schedule=调度配置 jobinfo_conf_job=任务配置 jobinfo_conf_advanced=高级配置 ## job log joblog_name=调度日志 joblog_status=状态 joblog_status_all=全部 joblog_status_suc=成功 joblog_status_fail=失败 joblog_status_running=进行中 joblog_field_triggerTime=调度时间 joblog_field_triggerCode=调度结果 joblog_field_triggerMsg=调度备注 joblog_field_handleTime=执行时间 joblog_field_handleCode=执行结果 joblog_field_handleMsg=执行备注 joblog_field_executorAddress=执行器地址 joblog_clean=清理 joblog_clean_log=日志清理 joblog_clean_type=清理方式 joblog_clean_type_1=清理一个月之前日志数据 joblog_clean_type_2=清理三个月之前日志数据 joblog_clean_type_3=清理六个月之前日志数据 joblog_clean_type_4=清理一年之前日志数据 joblog_clean_type_5=清理一千条以前日志数据 joblog_clean_type_6=清理一万条以前日志数据 joblog_clean_type_7=清理三万条以前日志数据 joblog_clean_type_8=清理十万条以前日志数据 joblog_clean_type_9=清理所有日志数据 joblog_clean_type_unvalid=清理类型参数异常 joblog_handleCode_200=成功 joblog_handleCode_500=失败 joblog_handleCode_502=失败(超时) joblog_kill_log=终止任务 joblog_kill_log_limit=调度失败,无法终止日志 joblog_kill_log_byman=人为操作,主动终止 joblog_lost_fail=任务结果丢失,标记失败 joblog_rolling_log=执行日志 joblog_rolling_log_refresh=刷新 joblog_rolling_log_triggerfail=任务发起调度失败,无法查看执行日志 joblog_rolling_log_failoften=终止请求Rolling日志,请求失败次数超上限,可刷新页面重新加载日志 joblog_logid_unvalid=日志ID非法 ## job group jobgroup_name=执行器管理 jobgroup_list=执行器列表 jobgroup_add=新增执行器 jobgroup_edit=编辑执行器 jobgroup_del=删除执行器 jobgroup_field_title=名称 jobgroup_field_addressType=注册方式 jobgroup_field_addressType_0=自动注册 jobgroup_field_addressType_1=手动录入 jobgroup_field_addressType_limit=手动录入注册方式,机器地址不可为空 jobgroup_field_registryList=机器地址 jobgroup_field_registryList_unvalid=机器地址格式非法 jobgroup_field_registryList_placeholder=请输入执行器地址列表,多地址逗号分隔 jobgroup_field_appname_limit=限制以小写字母开头,由小写字母、数字和中划线组成 jobgroup_field_appname_length=AppName长度限制为4~64 jobgroup_field_title_length=名称长度限制为4~12 jobgroup_field_order_digits=请输入整数 jobgroup_field_orderrange=取值范围为1~1000 jobgroup_del_limit_0=拒绝删除,该执行器使用中 jobgroup_del_limit_1=拒绝删除, 系统至少保留一个执行器 jobgroup_empty=不存在有效执行器,请联系管理员 ## job conf jobconf_block_SERIAL_EXECUTION=单机串行 jobconf_block_DISCARD_LATER=丢弃后续调度 jobconf_block_COVER_EARLY=覆盖之前调度 jobconf_route_first=第一个 jobconf_route_last=最后一个 jobconf_route_round=轮询 jobconf_route_random=随机 jobconf_route_consistenthash=一致性HASH jobconf_route_lfu=最不经常使用 jobconf_route_lru=最近最久未使用 jobconf_route_failover=故障转移 jobconf_route_busyover=忙碌转移 jobconf_route_shard=分片广播 jobconf_idleBeat=空闲检测 jobconf_beat=心跳检测 jobconf_monitor=任务调度中心监控报警 jobconf_monitor_detail=监控告警明细 jobconf_monitor_alarm_title=告警类型 jobconf_monitor_alarm_type=调度失败 jobconf_monitor_alarm_content=告警内容 jobconf_trigger_admin_adress=调度机器 jobconf_trigger_exe_regtype=执行器-注册方式 jobconf_trigger_exe_regaddress=执行器-地址列表 jobconf_trigger_address_empty=调度失败:执行器地址为空 jobconf_trigger_run=触发调度 jobconf_trigger_child_run=触发子任务 jobconf_callback_child_msg1={0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4}
jobconf_callback_child_msg2={0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误
jobconf_trigger_type=任务触发类型 jobconf_trigger_type_cron=Cron触发 jobconf_trigger_type_manual=手动触发 jobconf_trigger_type_parent=父任务触发 jobconf_trigger_type_api=API触发 jobconf_trigger_type_retry=失败重试触发 jobconf_trigger_type_misfire=调度过期补偿 ## user user_manage=用户管理 user_username=账号 user_password=密码 user_role=角色 user_role_admin=管理员 user_role_normal=普通用户 user_permission=权限 user_add=新增用户 user_update=更新用户 user_username_repeat=账号重复 user_username_valid=限制以小写字母开头,由小写字母、数字组成 user_password_update_placeholder=请输入新密码,为空则不更新密码 user_update_loginuser_limit=禁止操作当前登录账号 ## help job_help=使用教程 job_help_document=官方文档 ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties ================================================ admin_name=任務調度中心 admin_name_full=分布式任務調度平臺XXL-JOB admin_version=2.4.0 admin_i18n= ## system system_tips=系統提示 system_ok=確定 system_close=關閉 system_save=儲存 system_cancel=取消 system_search=搜尋 system_status=狀態 system_opt=操作 system_please_input=請輸入 system_please_choose=请選擇 system_success=成功 system_fail=失敗 system_add_suc=新增成功 system_add_fail=新增失敗 system_update_suc=更新成功 system_update_fail=更新失敗 system_all=全部 system_api_error=API錯誤 system_show=查看 system_empty=無 system_opt_suc=操作成功 system_opt_fail=操作失敗 system_opt_edit=編輯 system_opt_del=刪除 system_opt_copy=復制 system_unvalid=非法 system_not_found=不存在 system_nav=導航 system_digits=整數 system_lengh_limit=長度限制 system_permission_limit=權限控管 system_welcome=歡迎 ## daterangepicker daterangepicker_ranges_recent_hour=最近一小時 daterangepicker_ranges_today=今日 daterangepicker_ranges_yesterday=昨日 daterangepicker_ranges_this_month=本月 daterangepicker_ranges_last_month=上個月 daterangepicker_ranges_recent_week=最近一周 daterangepicker_ranges_recent_month=最近一月 daterangepicker_custom_name=自定義 daterangepicker_custom_starttime=起始時間 daterangepicker_custom_endtime=結束時間 daterangepicker_custom_daysofweek=日,一,二,三,四,五,六 daterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月 ## dataTable dataTable_sProcessing=處理中... dataTable_sLengthMenu=每頁 _MENU_ 條記錄 dataTable_sZeroRecords=沒有相符合記錄 dataTable_sInfo=第 _PAGE_ 頁 ( 總共 _PAGES_ 頁,_TOTAL_ 條記錄 ) dataTable_sInfoEmpty=無記錄 dataTable_sInfoFiltered=(由 _MAX_ 項結果過濾) dataTable_sSearch=搜尋 dataTable_sEmptyTable=表中資料為空 dataTable_sLoadingRecords=載入中... dataTable_sFirst=首頁 dataTable_sPrevious=上頁 dataTable_sNext=下頁 dataTable_sLast=末頁 dataTable_sSortAscending=: 以升幂排序此列 dataTable_sSortDescending=: 以降幂排序此列 ## login login_btn=登入 login_remember_me=記住密碼 login_username_placeholder=請輸入登入帳號 login_password_placeholder=請輸入登入密碼 login_username_empty=請輸入登入帳號 login_username_lt_4=登入帳號不應低於4位數 login_password_empty=請輸入登入密碼 login_password_lt_4=登入密碼不應低於4位數 login_success=登入成功 login_fail=登入失敗 login_param_empty=帳號或密碼為空值 login_param_unvalid=帳號或密碼錯誤 ## logout logout_btn=登出 logout_confirm=確認登出? logout_success=登出成功 logout_fail=登出失敗 ## change pwd change_pwd=修改密碼 change_pwd_suc_to_logout=修改密碼成功,即將登出 change_pwd_field_newpwd=新密碼 ## dashboard job_dashboard_name=運行報表 job_dashboard_job_num=任務數量 job_dashboard_job_num_tip=調度中心運行的任務數量 job_dashboard_trigger_num=調度次數 job_dashboard_trigger_num_tip=調度中心觸發的調度次數 job_dashboard_jobgroup_num=執行器數量 job_dashboard_jobgroup_num_tip=調度中心在線的執行器機器數量 job_dashboard_report=調度報表 job_dashboard_report_loaddata_fail=調度報表資料加載異常 job_dashboard_date_report=日期分布圖 job_dashboard_rate_report=成功比例圖 ## job info jobinfo_name=任務管理 jobinfo_job=任務 jobinfo_field_add=新增 jobinfo_field_update=更新任務 jobinfo_field_id=任務ID jobinfo_field_jobgroup=執行器 jobinfo_field_jobdesc=任務描述 jobinfo_field_gluetype=運行模式 jobinfo_field_executorparam=任務參數 jobinfo_field_author=負責人 jobinfo_field_timeout=任務超時秒數 jobinfo_field_alarmemail=告警郵件 jobinfo_field_alarmemail_placeholder=輸入多個告警郵件地址,請以逗號分隔 jobinfo_field_executorRouteStrategy=路由策略 jobinfo_field_childJobId=子任務ID jobinfo_field_childJobId_placeholder=輸入子任務ID,如有多個請以逗號分隔 jobinfo_field_executorBlockStrategy=阻塞處理策略 jobinfo_field_executorFailRetryCount=失敗重試次數 jobinfo_field_executorFailRetryCount_placeholder=失敗重試次數,大於零時生效 jobinfo_script_location=腳本位置 jobinfo_shard_index=分片序號 jobinfo_shard_total=分片總數 jobinfo_opt_stop=停止 jobinfo_opt_start=啟動 jobinfo_opt_log=查詢日誌 jobinfo_opt_run=執行一次 jobinfo_opt_run_tips=請輸入本次執行的機器地址,為空則從執行器獲取 jobinfo_opt_registryinfo=注冊節點 jobinfo_opt_next_time=下次執行時間 jobinfo_glue_remark=源碼備註 jobinfo_glue_remark_limit=源碼備註長度限制為4~100 jobinfo_glue_rollback=版本回復 jobinfo_glue_jobid_unvalid=任務ID非法 jobinfo_glue_gluetype_unvalid=該任務非GLUE模式 jobinfo_field_executorTimeout_placeholder=任務超時時間,單位秒,大於零時生效 schedule_type=調度類型 schedule_type_none=無 schedule_type_cron=CRON schedule_type_fix_rate=固定速度 schedule_type_fix_delay=固定延遲 schedule_type_none_limit_start=當前調度類型禁止啟動 misfire_strategy=調度過期策略 misfire_strategy_do_nothing=忽略 misfire_strategy_fire_once_now=立即執行壹次 jobinfo_conf_base=基礎配置 jobinfo_conf_schedule=調度配置 jobinfo_conf_job=任務配置 jobinfo_conf_advanced=高級配置 ## job log joblog_name=調度日誌 joblog_status=狀態 joblog_status_all=全部 joblog_status_suc=成功 joblog_status_fail=失敗 joblog_status_running=進行中 joblog_field_triggerTime=調度時間 joblog_field_triggerCode=調度結果 joblog_field_triggerMsg=調度備註 joblog_field_handleTime=執行時間 joblog_field_handleCode=執行结果 joblog_field_handleMsg=執行備註 joblog_field_executorAddress=執行器地址 joblog_clean=清理 joblog_clean_log=日誌清理 joblog_clean_type=清理方式 joblog_clean_type_1=清理一個月之前日誌資料 joblog_clean_type_2=清理三個月之前日誌資料 joblog_clean_type_3=清理六個月之前日誌資料 joblog_clean_type_4=清理一年之前日誌資料 joblog_clean_type_5=清理一千條以前日誌資料 joblog_clean_type_6=清理一萬條以前日誌資料 joblog_clean_type_7=清理三萬條以前日誌資料 joblog_clean_type_8=清理十萬條以前日誌資料 joblog_clean_type_9=清理所有日誌資料 joblog_clean_type_unvalid=清理類型參数異常 joblog_handleCode_200=成功 joblog_handleCode_500=失敗 joblog_handleCode_502=失敗(超時) joblog_kill_log=终止任務 joblog_kill_log_limit=調度失敗,無法终止日誌 joblog_kill_log_byman=人為操作,主動終止 joblog_lost_fail=任務結果丟失,標記失敗 joblog_rolling_log=執行日誌 joblog_rolling_log_refresh=更新 joblog_rolling_log_triggerfail=任務發起調度失敗,無法查看執行日誌 joblog_rolling_log_failoften=終止請求Rolling日誌,請求失敗次數超上限,可刷新頁面重新加載日誌 joblog_logid_unvalid=日誌ID非法 ## job group jobgroup_name=執行器管理 jobgroup_list=執行器列表 jobgroup_add=新增執行器 jobgroup_edit=編輯執行器 jobgroup_del=刪除執行器 jobgroup_field_title=名稱 jobgroup_field_addressType=注冊方式 jobgroup_field_addressType_0=自動注冊 jobgroup_field_addressType_1=手動登錄 jobgroup_field_addressType_limit=手動登錄注冊方式,機器地址不可為空 jobgroup_field_registryList=機器地址 jobgroup_field_registryList_unvalid=機器地址格式非法 jobgroup_field_registryList_placeholder=請輸入執行器地址列表,多個地址請以逗號分隔 jobgroup_field_appname_limit=限制以小寫字母開頭,由小寫字母、數字和中划線組成 jobgroup_field_appname_length=AppName長度限制為4~64 jobgroup_field_title_length=名稱長度限制為4~12 jobgroup_field_order_digits=請輸入整數 jobgroup_field_orderrange=取值範圍為1~1000 jobgroup_del_limit_0=拒絕刪除,該執行器使用中 jobgroup_del_limit_1=拒絕删除,系统至少保留一個執行器 jobgroup_empty=不存在有效執行器,請聯絡系統管理員 ## job conf jobconf_block_SERIAL_EXECUTION=單機串行 jobconf_block_DISCARD_LATER=丢棄后續調度 jobconf_block_COVER_EARLY=覆蓋之前調度 jobconf_route_first=第一個 jobconf_route_last=最後一個 jobconf_route_round=輪詢 jobconf_route_random=隨機 jobconf_route_consistenthash=一致性HASH jobconf_route_lfu=最不經常使用 jobconf_route_lru=最近最久未使用 jobconf_route_failover=故障轉移 jobconf_route_busyover=忙碌轉移 jobconf_route_shard=分片廣播 jobconf_idleBeat=空閒檢測 jobconf_beat=心跳檢測 jobconf_monitor=任務調度中心監控告警 jobconf_monitor_detail=監控告警明细 jobconf_monitor_alarm_title=告警類型 jobconf_monitor_alarm_type=調度失敗 jobconf_monitor_alarm_content=告警内容 jobconf_trigger_admin_adress=調度機器 jobconf_trigger_exe_regtype=執行器-注冊方式 jobconf_trigger_exe_regaddress=執行器-地址列表 jobconf_trigger_address_empty=調度失敗:執行器地址為空 jobconf_trigger_run=觸發調度 jobconf_trigger_child_run=觸發子任務 jobconf_callback_child_msg1={0}/{1} [任務ID={2}], 觸發{3}, 觸發備註: {4}
jobconf_callback_child_msg2={0}/{1} [任務ID={2}], 觸發失败, 觸發備註: 任務ID格式錯誤
jobconf_trigger_type=任務觸發類型 jobconf_trigger_type_cron=Cron觸發 jobconf_trigger_type_manual=手動觸發 jobconf_trigger_type_parent=父任務觸發 jobconf_trigger_type_api=API觸發 jobconf_trigger_type_retry=失敗重試觸發 jobconf_trigger_type_misfire=調度過期補償 ## user user_manage=用户管理 user_username=帳號 user_password=密碼 user_role=角色 user_role_admin=管理員 user_role_normal=普通用戶 user_permission=權限 user_add=新增用戶 user_update=更新用戶 user_username_repeat=帳號重複 user_username_valid=限制以小寫字母開頭,由小寫字母、數字組成 user_password_update_placeholder=請輸入新密碼,為空則不更新密碼 user_update_loginuser_limit=禁止操作當前登入帳號 ## help job_help=使用教程 job_help_document=官方文件 ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback-plus.xml ================================================ logback ${console.log.pattern} utf-8 ${log.path}.log ${log.path}.%d{yyyy-MM-dd}.log 60 ${log.pattern} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobGroupMapper.xml ================================================ t.id, t.app_name, t.title, t.address_type, t.address_list, t.update_time INSERT INTO xxl_job_group ( `app_name`, `title`, `address_type`, `address_list`, `update_time`) values ( #{appname}, #{title}, #{addressType}, #{addressList}, #{updateTime} ); UPDATE xxl_job_group SET `app_name` = #{appname}, `title` = #{title}, `address_type` = #{addressType}, `address_list` = #{addressList}, `update_time` = #{updateTime} WHERE id = #{id} DELETE FROM xxl_job_group WHERE id = #{id} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml ================================================ t.id, t.job_group, t.job_desc, t.add_time, t.update_time, t.author, t.alarm_email, t.schedule_type, t.schedule_conf, t.misfire_strategy, t.executor_route_strategy, t.executor_handler, t.executor_param, t.executor_block_strategy, t.executor_timeout, t.executor_fail_retry_count, t.glue_type, t.glue_source, t.glue_remark, t.glue_updatetime, t.child_jobid, t.trigger_status, t.trigger_last_time, t.trigger_next_time INSERT INTO xxl_job_info ( job_group, job_desc, add_time, update_time, author, alarm_email, schedule_type, schedule_conf, misfire_strategy, executor_route_strategy, executor_handler, executor_param, executor_block_strategy, executor_timeout, executor_fail_retry_count, glue_type, glue_source, glue_remark, glue_updatetime, child_jobid, trigger_status, trigger_last_time, trigger_next_time ) VALUES ( #{jobGroup}, #{jobDesc}, #{addTime}, #{updateTime}, #{author}, #{alarmEmail}, #{scheduleType}, #{scheduleConf}, #{misfireStrategy}, #{executorRouteStrategy}, #{executorHandler}, #{executorParam}, #{executorBlockStrategy}, #{executorTimeout}, #{executorFailRetryCount}, #{glueType}, #{glueSource}, #{glueRemark}, #{glueUpdatetime}, #{childJobId}, #{triggerStatus}, #{triggerLastTime}, #{triggerNextTime} ); UPDATE xxl_job_info SET job_group = #{jobGroup}, job_desc = #{jobDesc}, update_time = #{updateTime}, author = #{author}, alarm_email = #{alarmEmail}, schedule_type = #{scheduleType}, schedule_conf = #{scheduleConf}, misfire_strategy = #{misfireStrategy}, executor_route_strategy = #{executorRouteStrategy}, executor_handler = #{executorHandler}, executor_param = #{executorParam}, executor_block_strategy = #{executorBlockStrategy}, executor_timeout = ${executorTimeout}, executor_fail_retry_count = ${executorFailRetryCount}, glue_type = #{glueType}, glue_source = #{glueSource}, glue_remark = #{glueRemark}, glue_updatetime = #{glueUpdatetime}, child_jobid = #{childJobId}, trigger_status = #{triggerStatus}, trigger_last_time = #{triggerLastTime}, trigger_next_time = #{triggerNextTime} WHERE id = #{id} DELETE FROM xxl_job_info WHERE id = #{id} UPDATE xxl_job_info SET trigger_last_time = #{triggerLastTime}, trigger_next_time = #{triggerNextTime}, trigger_status = #{triggerStatus} WHERE id = #{id} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogGlueMapper.xml ================================================ t.id, t.job_id, t.glue_type, t.glue_source, t.glue_remark, t.add_time, t.update_time INSERT INTO xxl_job_logglue ( `job_id`, `glue_type`, `glue_source`, `glue_remark`, `add_time`, `update_time` ) VALUES ( #{jobId}, #{glueType}, #{glueSource}, #{glueRemark}, #{addTime}, #{updateTime} ); DELETE FROM xxl_job_logglue WHERE id NOT in( SELECT id FROM( SELECT id FROM xxl_job_logglue WHERE `job_id` = #{jobId} ORDER BY update_time desc LIMIT 0, #{limit} ) t1 ) AND `job_id` = #{jobId} DELETE FROM xxl_job_logglue WHERE `job_id` = #{jobId} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml ================================================ t.id, t.job_group, t.job_id, t.executor_address, t.executor_handler, t.executor_param, t.executor_sharding_param, t.executor_fail_retry_count, t.trigger_time, t.trigger_code, t.trigger_msg, t.handle_time, t.handle_code, t.handle_msg, t.alarm_status INSERT INTO xxl_job_log ( `job_group`, `job_id`, `trigger_time`, `trigger_code`, `handle_code` ) VALUES ( #{jobGroup}, #{jobId}, #{triggerTime}, #{triggerCode}, #{handleCode} ); UPDATE xxl_job_log SET `trigger_time`= #{triggerTime}, `trigger_code`= #{triggerCode}, `trigger_msg`= #{triggerMsg}, `executor_address`= #{executorAddress}, `executor_handler`=#{executorHandler}, `executor_param`= #{executorParam}, `executor_sharding_param`= #{executorShardingParam}, `executor_fail_retry_count`= #{executorFailRetryCount} WHERE `id`= #{id} UPDATE xxl_job_log SET `handle_time`= #{handleTime}, `handle_code`= #{handleCode}, `handle_msg`= #{handleMsg} WHERE `id`= #{id} delete from xxl_job_log WHERE job_id = #{jobId} delete from xxl_job_log WHERE id in #{item} UPDATE xxl_job_log SET `alarm_status` = #{newAlarmStatus} WHERE `id`= #{logId} AND `alarm_status` = #{oldAlarmStatus} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogReportMapper.xml ================================================ t.id, t.trigger_day, t.running_count, t.suc_count, t.fail_count INSERT INTO xxl_job_log_report ( `trigger_day`, `running_count`, `suc_count`, `fail_count` ) VALUES ( #{triggerDay}, #{runningCount}, #{sucCount}, #{failCount} ); UPDATE xxl_job_log_report SET `running_count` = #{runningCount}, `suc_count` = #{sucCount}, `fail_count` = #{failCount} WHERE `trigger_day` = #{triggerDay} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobRegistryMapper.xml ================================================ t.id, t.registry_group, t.registry_key, t.registry_value, t.update_time DELETE FROM xxl_job_registry WHERE id in #{item} UPDATE xxl_job_registry SET `update_time` = #{updateTime} WHERE `registry_group` = #{registryGroup} AND `registry_key` = #{registryKey} AND `registry_value` = #{registryValue} INSERT INTO xxl_job_registry( `registry_group` , `registry_key` , `registry_value`, `update_time`) VALUES( #{registryGroup} , #{registryKey} , #{registryValue}, #{updateTime}) DELETE FROM xxl_job_registry WHERE registry_group = #{registryGroup} AND registry_key = #{registryKey} AND registry_value = #{registryValue} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobUserMapper.xml ================================================ t.id, t.username, t.password, t.role, t.permission INSERT INTO xxl_job_user ( username, password, role, permission ) VALUES ( #{username}, #{password}, #{role}, #{permission} ); UPDATE xxl_job_user SET password = #{password}, role = #{role}, permission = #{permission} WHERE id = #{id} DELETE FROM xxl_job_user WHERE id = #{id} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/PACE/themes/blue/pace-theme-flash.css ================================================ /* This is a compiled file, you should be editing the file in the templates directory */ .pace { -webkit-pointer-events: none; pointer-events: none; -webkit-user-select: none; -moz-user-select: none; user-select: none; } .pace-inactive { display: none; } .pace .pace-progress { background: #2299dd; position: fixed; z-index: 2000; top: 0; right: 100%; width: 100%; height: 2px; } .pace .pace-progress-inner { display: block; position: absolute; right: 0px; width: 100px; height: 100%; box-shadow: 0 0 10px #2299dd, 0 0 5px #2299dd; opacity: 1.0; -webkit-transform: rotate(3deg) translate(0px, -4px); -moz-transform: rotate(3deg) translate(0px, -4px); -ms-transform: rotate(3deg) translate(0px, -4px); -o-transform: rotate(3deg) translate(0px, -4px); transform: rotate(3deg) translate(0px, -4px); } .pace .pace-activity { display: block; position: fixed; z-index: 2000; top: 15px; right: 15px; width: 14px; height: 14px; border: solid 2px transparent; border-top-color: #2299dd; border-left-color: #2299dd; border-radius: 10px; -webkit-animation: pace-spinner 400ms linear infinite; -moz-animation: pace-spinner 400ms linear infinite; -ms-animation: pace-spinner 400ms linear infinite; -o-animation: pace-spinner 400ms linear infinite; animation: pace-spinner 400ms linear infinite; } @-webkit-keyframes pace-spinner { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } @-moz-keyframes pace-spinner { 0% { -moz-transform: rotate(0deg); transform: rotate(0deg); } 100% { -moz-transform: rotate(360deg); transform: rotate(360deg); } } @-o-keyframes pace-spinner { 0% { -o-transform: rotate(0deg); transform: rotate(0deg); } 100% { -o-transform: rotate(360deg); transform: rotate(360deg); } } @-ms-keyframes pace-spinner { 0% { -ms-transform: rotate(0deg); transform: rotate(0deg); } 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); } } @keyframes pace-spinner { 0% { transform: rotate(0deg); transform: rotate(0deg); } 100% { transform: rotate(360deg); transform: rotate(360deg); } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.css ================================================ .daterangepicker { position: absolute; color: inherit; background-color: #fff; border-radius: 4px; width: 278px; padding: 4px; margin-top: 1px; top: 100px; left: 20px; /* Calendars */ } .daterangepicker:before, .daterangepicker:after { position: absolute; display: inline-block; border-bottom-color: rgba(0, 0, 0, 0.2); content: ''; } .daterangepicker:before { top: -7px; border-right: 7px solid transparent; border-left: 7px solid transparent; border-bottom: 7px solid #ccc; } .daterangepicker:after { top: -6px; border-right: 6px solid transparent; border-bottom: 6px solid #fff; border-left: 6px solid transparent; } .daterangepicker.opensleft:before { right: 9px; } .daterangepicker.opensleft:after { right: 10px; } .daterangepicker.openscenter:before { left: 0; right: 0; width: 0; margin-left: auto; margin-right: auto; } .daterangepicker.openscenter:after { left: 0; right: 0; width: 0; margin-left: auto; margin-right: auto; } .daterangepicker.opensright:before { left: 9px; } .daterangepicker.opensright:after { left: 10px; } .daterangepicker.dropup { margin-top: -5px; } .daterangepicker.dropup:before { top: initial; bottom: -7px; border-bottom: initial; border-top: 7px solid #ccc; } .daterangepicker.dropup:after { top: initial; bottom: -6px; border-bottom: initial; border-top: 6px solid #fff; } .daterangepicker.dropdown-menu { max-width: none; z-index: 3001; } .daterangepicker.single .ranges, .daterangepicker.single .calendar { float: none; } .daterangepicker.show-calendar .calendar { display: block; } .daterangepicker .calendar { display: none; max-width: 270px; margin: 4px; } .daterangepicker .calendar.single .calendar-table { border: none; } .daterangepicker .calendar th, .daterangepicker .calendar td { white-space: nowrap; text-align: center; min-width: 32px; } .daterangepicker .calendar-table { border: 1px solid #fff; padding: 4px; border-radius: 4px; background-color: #fff; } .daterangepicker table { width: 100%; margin: 0; } .daterangepicker td, .daterangepicker th { text-align: center; width: 20px; height: 20px; border-radius: 4px; border: 1px solid transparent; white-space: nowrap; cursor: pointer; } .daterangepicker td.available:hover, .daterangepicker th.available:hover { background-color: #eee; border-color: transparent; color: inherit; } .daterangepicker td.week, .daterangepicker th.week { font-size: 80%; color: #ccc; } .daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date { background-color: #fff; border-color: transparent; color: #999; } .daterangepicker td.in-range { background-color: #ebf4f8; border-color: transparent; color: #000; border-radius: 0; } .daterangepicker td.start-date { border-radius: 4px 0 0 4px; } .daterangepicker td.end-date { border-radius: 0 4px 4px 0; } .daterangepicker td.start-date.end-date { border-radius: 4px; } .daterangepicker td.active, .daterangepicker td.active:hover { background-color: #357ebd; border-color: transparent; color: #fff; } .daterangepicker th.month { width: auto; } .daterangepicker td.disabled, .daterangepicker option.disabled { color: #999; cursor: not-allowed; text-decoration: line-through; } .daterangepicker select.monthselect, .daterangepicker select.yearselect { font-size: 12px; padding: 1px; height: auto; margin: 0; cursor: default; } .daterangepicker select.monthselect { margin-right: 2%; width: 56%; } .daterangepicker select.yearselect { width: 40%; } .daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect { width: 50px; margin-bottom: 0; } .daterangepicker .input-mini { border: 1px solid #ccc; border-radius: 4px; color: #555; height: 30px; line-height: 30px; display: block; vertical-align: middle; margin: 0 0 5px 0; padding: 0 6px 0 28px; width: 100%; } .daterangepicker .input-mini.active { border: 1px solid #08c; border-radius: 4px; } .daterangepicker .daterangepicker_input { position: relative; } .daterangepicker .daterangepicker_input i { position: absolute; left: 8px; top: 8px; } .daterangepicker.rtl .input-mini { padding-right: 28px; padding-left: 6px; } .daterangepicker.rtl .daterangepicker_input i { left: auto; right: 8px; } .daterangepicker .calendar-time { text-align: center; margin: 5px auto; line-height: 30px; position: relative; padding-left: 28px; } .daterangepicker .calendar-time select.disabled { color: #ccc; cursor: not-allowed; } .ranges { font-size: 11px; float: none; margin: 4px; text-align: left; } .ranges ul { list-style: none; margin: 0 auto; padding: 0; width: 100%; } .ranges li { font-size: 13px; background-color: #f5f5f5; border: 1px solid #f5f5f5; border-radius: 4px; color: #08c; padding: 3px 12px; margin-bottom: 8px; cursor: pointer; } .ranges li:hover { background-color: #08c; border: 1px solid #08c; color: #fff; } .ranges li.active { background-color: #08c; border: 1px solid #08c; color: #fff; } /* Larger Screen Styling */ @media (min-width: 564px) { .daterangepicker { width: auto; } .daterangepicker .ranges ul { width: 160px; } .daterangepicker.single .ranges ul { width: 100%; } .daterangepicker.single .calendar.left { clear: none; } .daterangepicker.single.ltr .ranges, .daterangepicker.single.ltr .calendar { float: left; } .daterangepicker.single.rtl .ranges, .daterangepicker.single.rtl .calendar { float: right; } .daterangepicker.ltr { direction: ltr; text-align: left; } .daterangepicker.ltr .calendar.left { clear: left; margin-right: 0; } .daterangepicker.ltr .calendar.left .calendar-table { border-right: none; border-top-right-radius: 0; border-bottom-right-radius: 0; } .daterangepicker.ltr .calendar.right { margin-left: 0; } .daterangepicker.ltr .calendar.right .calendar-table { border-left: none; border-top-left-radius: 0; border-bottom-left-radius: 0; } .daterangepicker.ltr .left .daterangepicker_input { padding-right: 12px; } .daterangepicker.ltr .calendar.left .calendar-table { padding-right: 12px; } .daterangepicker.ltr .ranges, .daterangepicker.ltr .calendar { float: left; } .daterangepicker.rtl { direction: rtl; text-align: right; } .daterangepicker.rtl .calendar.left { clear: right; margin-left: 0; } .daterangepicker.rtl .calendar.left .calendar-table { border-left: none; border-top-left-radius: 0; border-bottom-left-radius: 0; } .daterangepicker.rtl .calendar.right { margin-right: 0; } .daterangepicker.rtl .calendar.right .calendar-table { border-right: none; border-top-right-radius: 0; border-bottom-right-radius: 0; } .daterangepicker.rtl .left .daterangepicker_input { padding-left: 12px; } .daterangepicker.rtl .calendar.left .calendar-table { padding-left: 12px; } .daterangepicker.rtl .ranges, .daterangepicker.rtl .calendar { text-align: right; float: right; } } @media (min-width: 730px) { .daterangepicker .ranges { width: auto; } .daterangepicker.ltr .ranges { float: left; } .daterangepicker.rtl .ranges { float: right; } .daterangepicker .calendar.left { clear: none !important; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/bootstrap-daterangepicker/daterangepicker.js ================================================ /** * @version: 2.1.27 * @author: Dan Grossman http://www.dangrossman.info/ * @copyright: Copyright (c) 2012-2017 Dan Grossman. All rights reserved. * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php * @website: http://www.daterangepicker.com/ */ // Follow the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Make globaly available as well define(['moment', 'jquery'], function (moment, jquery) { if (!jquery.fn) jquery.fn = {}; // webpack server rendering return factory(moment, jquery); }); } else if (typeof module === 'object' && module.exports) { // Node / Browserify //isomorphic issue var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined; if (!jQuery) { jQuery = require('jquery'); if (!jQuery.fn) jQuery.fn = {}; } var moment = (typeof window != 'undefined' && typeof window.moment != 'undefined') ? window.moment : require('moment'); module.exports = factory(moment, jQuery); } else { // Browser globals root.daterangepicker = factory(root.moment, root.jQuery); } }(this, function(moment, $) { var DateRangePicker = function(element, options, cb) { //default settings for options this.parentEl = 'body'; this.element = $(element); this.startDate = moment().startOf('day'); this.endDate = moment().endOf('day'); this.minDate = false; this.maxDate = false; this.dateLimit = false; this.autoApply = false; this.singleDatePicker = false; this.showDropdowns = false; this.showWeekNumbers = false; this.showISOWeekNumbers = false; this.showCustomRangeLabel = true; this.timePicker = false; this.timePicker24Hour = false; this.timePickerIncrement = 1; this.timePickerSeconds = false; this.linkedCalendars = true; this.autoUpdateInput = true; this.alwaysShowCalendars = false; this.ranges = {}; this.opens = 'right'; if (this.element.hasClass('pull-right')) this.opens = 'left'; this.drops = 'down'; if (this.element.hasClass('dropup')) this.drops = 'up'; this.buttonClasses = 'btn btn-sm'; this.applyClass = 'btn-success'; this.cancelClass = 'btn-default'; this.locale = { direction: 'ltr', format: moment.localeData().longDateFormat('L'), separator: ' - ', applyLabel: 'Apply', cancelLabel: 'Cancel', weekLabel: 'W', customRangeLabel: 'Custom Range', daysOfWeek: moment.weekdaysMin(), monthNames: moment.monthsShort(), firstDay: moment.localeData().firstDayOfWeek() }; this.callback = function() { }; //some state information this.isShowing = false; this.leftCalendar = {}; this.rightCalendar = {}; //custom options from user if (typeof options !== 'object' || options === null) options = {}; //allow setting options with data attributes //data-api options will be overwritten with custom javascript options options = $.extend(this.element.data(), options); //html template for the picker UI if (typeof options.template !== 'string' && !(options.template instanceof $)) options.template = '

'; this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl); this.container = $(options.template).appendTo(this.parentEl); // // handle all the possible options overriding defaults // if (typeof options.locale === 'object') { if (typeof options.locale.direction === 'string') this.locale.direction = options.locale.direction; if (typeof options.locale.format === 'string') this.locale.format = options.locale.format; if (typeof options.locale.separator === 'string') this.locale.separator = options.locale.separator; if (typeof options.locale.daysOfWeek === 'object') this.locale.daysOfWeek = options.locale.daysOfWeek.slice(); if (typeof options.locale.monthNames === 'object') this.locale.monthNames = options.locale.monthNames.slice(); if (typeof options.locale.firstDay === 'number') this.locale.firstDay = options.locale.firstDay; if (typeof options.locale.applyLabel === 'string') this.locale.applyLabel = options.locale.applyLabel; if (typeof options.locale.cancelLabel === 'string') this.locale.cancelLabel = options.locale.cancelLabel; if (typeof options.locale.weekLabel === 'string') this.locale.weekLabel = options.locale.weekLabel; if (typeof options.locale.customRangeLabel === 'string'){ //Support unicode chars in the custom range name. var elem = document.createElement('textarea'); elem.innerHTML = options.locale.customRangeLabel; var rangeHtml = elem.value; this.locale.customRangeLabel = rangeHtml; } } this.container.addClass(this.locale.direction); if (typeof options.startDate === 'string') this.startDate = moment(options.startDate, this.locale.format); if (typeof options.endDate === 'string') this.endDate = moment(options.endDate, this.locale.format); if (typeof options.minDate === 'string') this.minDate = moment(options.minDate, this.locale.format); if (typeof options.maxDate === 'string') this.maxDate = moment(options.maxDate, this.locale.format); if (typeof options.startDate === 'object') this.startDate = moment(options.startDate); if (typeof options.endDate === 'object') this.endDate = moment(options.endDate); if (typeof options.minDate === 'object') this.minDate = moment(options.minDate); if (typeof options.maxDate === 'object') this.maxDate = moment(options.maxDate); // sanity check for bad options if (this.minDate && this.startDate.isBefore(this.minDate)) this.startDate = this.minDate.clone(); // sanity check for bad options if (this.maxDate && this.endDate.isAfter(this.maxDate)) this.endDate = this.maxDate.clone(); if (typeof options.applyClass === 'string') this.applyClass = options.applyClass; if (typeof options.cancelClass === 'string') this.cancelClass = options.cancelClass; if (typeof options.dateLimit === 'object') this.dateLimit = options.dateLimit; if (typeof options.opens === 'string') this.opens = options.opens; if (typeof options.drops === 'string') this.drops = options.drops; if (typeof options.showWeekNumbers === 'boolean') this.showWeekNumbers = options.showWeekNumbers; if (typeof options.showISOWeekNumbers === 'boolean') this.showISOWeekNumbers = options.showISOWeekNumbers; if (typeof options.buttonClasses === 'string') this.buttonClasses = options.buttonClasses; if (typeof options.buttonClasses === 'object') this.buttonClasses = options.buttonClasses.join(' '); if (typeof options.showDropdowns === 'boolean') this.showDropdowns = options.showDropdowns; if (typeof options.showCustomRangeLabel === 'boolean') this.showCustomRangeLabel = options.showCustomRangeLabel; if (typeof options.singleDatePicker === 'boolean') { this.singleDatePicker = options.singleDatePicker; if (this.singleDatePicker) this.endDate = this.startDate.clone(); } if (typeof options.timePicker === 'boolean') this.timePicker = options.timePicker; if (typeof options.timePickerSeconds === 'boolean') this.timePickerSeconds = options.timePickerSeconds; if (typeof options.timePickerIncrement === 'number') this.timePickerIncrement = options.timePickerIncrement; if (typeof options.timePicker24Hour === 'boolean') this.timePicker24Hour = options.timePicker24Hour; if (typeof options.autoApply === 'boolean') this.autoApply = options.autoApply; if (typeof options.autoUpdateInput === 'boolean') this.autoUpdateInput = options.autoUpdateInput; if (typeof options.linkedCalendars === 'boolean') this.linkedCalendars = options.linkedCalendars; if (typeof options.isInvalidDate === 'function') this.isInvalidDate = options.isInvalidDate; if (typeof options.isCustomDate === 'function') this.isCustomDate = options.isCustomDate; if (typeof options.alwaysShowCalendars === 'boolean') this.alwaysShowCalendars = options.alwaysShowCalendars; // update day names order to firstDay if (this.locale.firstDay != 0) { var iterator = this.locale.firstDay; while (iterator > 0) { this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift()); iterator--; } } var start, end, range; //if no start/end dates set, check if an input element contains initial values if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') { if ($(this.element).is('input[type=text]')) { var val = $(this.element).val(), split = val.split(this.locale.separator); start = end = null; if (split.length == 2) { start = moment(split[0], this.locale.format); end = moment(split[1], this.locale.format); } else if (this.singleDatePicker && val !== "") { start = moment(val, this.locale.format); end = moment(val, this.locale.format); } if (start !== null && end !== null) { this.setStartDate(start); this.setEndDate(end); } } } if (typeof options.ranges === 'object') { for (range in options.ranges) { if (typeof options.ranges[range][0] === 'string') start = moment(options.ranges[range][0], this.locale.format); else start = moment(options.ranges[range][0]); if (typeof options.ranges[range][1] === 'string') end = moment(options.ranges[range][1], this.locale.format); else end = moment(options.ranges[range][1]); // If the start or end date exceed those allowed by the minDate or dateLimit // options, shorten the range to the allowable period. if (this.minDate && start.isBefore(this.minDate)) start = this.minDate.clone(); var maxDate = this.maxDate; if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate)) maxDate = start.clone().add(this.dateLimit); if (maxDate && end.isAfter(maxDate)) end = maxDate.clone(); // If the end of the range is before the minimum or the start of the range is // after the maximum, don't display this range option at all. if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day')) || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day'))) continue; //Support unicode chars in the range names. var elem = document.createElement('textarea'); elem.innerHTML = range; var rangeHtml = elem.value; this.ranges[rangeHtml] = [start, end]; } var list = '
    '; for (range in this.ranges) { list += '
  • ' + range + '
  • '; } if (this.showCustomRangeLabel) { list += '
  • ' + this.locale.customRangeLabel + '
  • '; } list += '
'; this.container.find('.ranges').prepend(list); } if (typeof cb === 'function') { this.callback = cb; } if (!this.timePicker) { this.startDate = this.startDate.startOf('day'); this.endDate = this.endDate.endOf('day'); this.container.find('.calendar-time').hide(); } //can't be used together for now if (this.timePicker && this.autoApply) this.autoApply = false; if (this.autoApply && typeof options.ranges !== 'object') { this.container.find('.ranges').hide(); } else if (this.autoApply) { this.container.find('.applyBtn, .cancelBtn').addClass('hide'); } if (this.singleDatePicker) { this.container.addClass('single'); this.container.find('.calendar.left').addClass('single'); this.container.find('.calendar.left').show(); this.container.find('.calendar.right').hide(); this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide(); if (this.timePicker) { this.container.find('.ranges ul').hide(); } else { this.container.find('.ranges').hide(); } } if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) { this.container.addClass('show-calendar'); } this.container.addClass('opens' + this.opens); //swap the position of the predefined ranges if opens right if (typeof options.ranges !== 'undefined' && this.opens == 'right') { this.container.find('.ranges').prependTo( this.container.find('.calendar.left').parent() ); } //apply CSS classes and labels to buttons this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses); if (this.applyClass.length) this.container.find('.applyBtn').addClass(this.applyClass); if (this.cancelClass.length) this.container.find('.cancelBtn').addClass(this.cancelClass); this.container.find('.applyBtn').html(this.locale.applyLabel); this.container.find('.cancelBtn').html(this.locale.cancelLabel); // // event listeners // this.container.find('.calendar') .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this)) .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this)) .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this)) .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this)) .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this)) .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this)) .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this)) .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this)) .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this)) .on('focus.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsFocused, this)) .on('blur.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsBlurred, this)) .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this)) .on('keydown.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsKeydown, this)); this.container.find('.ranges') .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this)) .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this)) .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this)) .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this)) .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this)); if (this.element.is('input') || this.element.is('button')) { this.element.on({ 'click.daterangepicker': $.proxy(this.show, this), 'focus.daterangepicker': $.proxy(this.show, this), 'keyup.daterangepicker': $.proxy(this.elementChanged, this), 'keydown.daterangepicker': $.proxy(this.keydown, this) //IE 11 compatibility }); } else { this.element.on('click.daterangepicker', $.proxy(this.toggle, this)); this.element.on('keydown.daterangepicker', $.proxy(this.toggle, this)); } // // if attached to a text input, set the initial value // if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); this.element.trigger('change'); } else if (this.element.is('input') && this.autoUpdateInput) { this.element.val(this.startDate.format(this.locale.format)); this.element.trigger('change'); } }; DateRangePicker.prototype = { constructor: DateRangePicker, setStartDate: function(startDate) { if (typeof startDate === 'string') this.startDate = moment(startDate, this.locale.format); if (typeof startDate === 'object') this.startDate = moment(startDate); if (!this.timePicker) this.startDate = this.startDate.startOf('day'); if (this.timePicker && this.timePickerIncrement) this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); if (this.minDate && this.startDate.isBefore(this.minDate)) { this.startDate = this.minDate.clone(); if (this.timePicker && this.timePickerIncrement) this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); } if (this.maxDate && this.startDate.isAfter(this.maxDate)) { this.startDate = this.maxDate.clone(); if (this.timePicker && this.timePickerIncrement) this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); } if (!this.isShowing) this.updateElement(); this.updateMonthsInView(); }, setEndDate: function(endDate) { if (typeof endDate === 'string') this.endDate = moment(endDate, this.locale.format); if (typeof endDate === 'object') this.endDate = moment(endDate); if (!this.timePicker) this.endDate = this.endDate.add(1,'d').startOf('day').subtract(1,'second'); if (this.timePicker && this.timePickerIncrement) this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); if (this.endDate.isBefore(this.startDate)) this.endDate = this.startDate.clone(); if (this.maxDate && this.endDate.isAfter(this.maxDate)) this.endDate = this.maxDate.clone(); if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate)) this.endDate = this.startDate.clone().add(this.dateLimit); this.previousRightTime = this.endDate.clone(); if (!this.isShowing) this.updateElement(); this.updateMonthsInView(); }, isInvalidDate: function() { return false; }, isCustomDate: function() { return false; }, updateView: function() { if (this.timePicker) { this.renderTimePicker('left'); this.renderTimePicker('right'); if (!this.endDate) { this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled'); } else { this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled'); } } if (this.endDate) { this.container.find('input[name="daterangepicker_end"]').removeClass('active'); this.container.find('input[name="daterangepicker_start"]').addClass('active'); } else { this.container.find('input[name="daterangepicker_end"]').addClass('active'); this.container.find('input[name="daterangepicker_start"]').removeClass('active'); } this.updateMonthsInView(); this.updateCalendars(); this.updateFormInputs(); }, updateMonthsInView: function() { if (this.endDate) { //if both dates are visible already, do nothing if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month && (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) && (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) ) { return; } this.leftCalendar.month = this.startDate.clone().date(2); if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) { this.rightCalendar.month = this.endDate.clone().date(2); } else { this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); } } else { if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) { this.leftCalendar.month = this.startDate.clone().date(2); this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); } } if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) { this.rightCalendar.month = this.maxDate.clone().date(2); this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month'); } }, updateCalendars: function() { if (this.timePicker) { var hour, minute, second; if (this.endDate) { hour = parseInt(this.container.find('.left .hourselect').val(), 10); minute = parseInt(this.container.find('.left .minuteselect').val(), 10); second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; if (!this.timePicker24Hour) { var ampm = this.container.find('.left .ampmselect').val(); if (ampm === 'PM' && hour < 12) hour += 12; if (ampm === 'AM' && hour === 12) hour = 0; } } else { hour = parseInt(this.container.find('.right .hourselect').val(), 10); minute = parseInt(this.container.find('.right .minuteselect').val(), 10); second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; if (!this.timePicker24Hour) { var ampm = this.container.find('.right .ampmselect').val(); if (ampm === 'PM' && hour < 12) hour += 12; if (ampm === 'AM' && hour === 12) hour = 0; } } this.leftCalendar.month.hour(hour).minute(minute).second(second); this.rightCalendar.month.hour(hour).minute(minute).second(second); } this.renderCalendar('left'); this.renderCalendar('right'); //highlight any predefined range matching the current start and end dates this.container.find('.ranges li').removeClass('active'); if (this.endDate == null) return; this.calculateChosenLabel(); }, renderCalendar: function(side) { // // Build the matrix of dates that will populate the calendar // var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar; var month = calendar.month.month(); var year = calendar.month.year(); var hour = calendar.month.hour(); var minute = calendar.month.minute(); var second = calendar.month.second(); var daysInMonth = moment([year, month]).daysInMonth(); var firstDay = moment([year, month, 1]); var lastDay = moment([year, month, daysInMonth]); var lastMonth = moment(firstDay).subtract(1, 'month').month(); var lastYear = moment(firstDay).subtract(1, 'month').year(); var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth(); var dayOfWeek = firstDay.day(); //initialize a 6 rows x 7 columns array for the calendar var calendar = []; calendar.firstDay = firstDay; calendar.lastDay = lastDay; for (var i = 0; i < 6; i++) { calendar[i] = []; } //populate the calendar with date objects var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1; if (startDay > daysInLastMonth) startDay -= 7; if (dayOfWeek == this.locale.firstDay) startDay = daysInLastMonth - 6; var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]); var col, row; for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) { if (i > 0 && col % 7 === 0) { col = 0; row++; } calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second); curDate.hour(12); if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') { calendar[row][col] = this.minDate.clone(); } if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') { calendar[row][col] = this.maxDate.clone(); } } //make the calendar object available to hoverDate/clickDate if (side == 'left') { this.leftCalendar.calendar = calendar; } else { this.rightCalendar.calendar = calendar; } // // Display the calendar // var minDate = side == 'left' ? this.minDate : this.startDate; var maxDate = this.maxDate; var selected = side == 'left' ? this.startDate : this.endDate; var arrow = this.locale.direction == 'ltr' ? {left: 'chevron-left', right: 'chevron-right'} : {left: 'chevron-right', right: 'chevron-left'}; var html = ''; html += ''; html += ''; // add empty cell for week number if (this.showWeekNumbers || this.showISOWeekNumbers) html += ''; if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) { html += ''; } else { html += ''; } var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY"); if (this.showDropdowns) { var currentMonth = calendar[1][1].month(); var currentYear = calendar[1][1].year(); var maxYear = (maxDate && maxDate.year()) || (currentYear + 5); var minYear = (minDate && minDate.year()) || (currentYear - 50); var inMinYear = currentYear == minYear; var inMaxYear = currentYear == maxYear; var monthHtml = '"; var yearHtml = ''; dateHtml = monthHtml + yearHtml; } html += ''; if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) { html += ''; } else { html += ''; } html += ''; html += ''; // add week number label if (this.showWeekNumbers || this.showISOWeekNumbers) html += ''; $.each(this.locale.daysOfWeek, function(index, dayOfWeek) { html += ''; }); html += ''; html += ''; html += ''; //adjust maxDate to reflect the dateLimit setting in order to //grey out end dates beyond the dateLimit if (this.endDate == null && this.dateLimit) { var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day'); if (!maxDate || maxLimit.isBefore(maxDate)) { maxDate = maxLimit; } } for (var row = 0; row < 6; row++) { html += ''; // add week number if (this.showWeekNumbers) html += ''; else if (this.showISOWeekNumbers) html += ''; for (var col = 0; col < 7; col++) { var classes = []; //highlight today's date if (calendar[row][col].isSame(new Date(), "day")) classes.push('today'); //highlight weekends if (calendar[row][col].isoWeekday() > 5) classes.push('weekend'); //grey out the dates in other months displayed at beginning and end of this calendar if (calendar[row][col].month() != calendar[1][1].month()) classes.push('off'); //don't allow selection of dates before the minimum date if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day')) classes.push('off', 'disabled'); //don't allow selection of dates after the maximum date if (maxDate && calendar[row][col].isAfter(maxDate, 'day')) classes.push('off', 'disabled'); //don't allow selection of date if a custom function decides it's invalid if (this.isInvalidDate(calendar[row][col])) classes.push('off', 'disabled'); //highlight the currently selected start date if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) classes.push('active', 'start-date'); //highlight the currently selected end date if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) classes.push('active', 'end-date'); //highlight dates in-between the selected dates if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate) classes.push('in-range'); //apply custom classes for this date var isCustom = this.isCustomDate(calendar[row][col]); if (isCustom !== false) { if (typeof isCustom === 'string') classes.push(isCustom); else Array.prototype.push.apply(classes, isCustom); } var cname = '', disabled = false; for (var i = 0; i < classes.length; i++) { cname += classes[i] + ' '; if (classes[i] == 'disabled') disabled = true; } if (!disabled) cname += 'available'; html += ''; } html += ''; } html += ''; html += '
' + dateHtml + '
' + this.locale.weekLabel + '' + dayOfWeek + '
' + calendar[row][0].week() + '' + calendar[row][0].isoWeek() + '' + calendar[row][col].date() + '
'; this.container.find('.calendar.' + side + ' .calendar-table').html(html); }, renderTimePicker: function(side) { // Don't bother updating the time picker if it's currently disabled // because an end date hasn't been clicked yet if (side == 'right' && !this.endDate) return; var html, selected, minDate, maxDate = this.maxDate; if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate))) maxDate = this.startDate.clone().add(this.dateLimit); if (side == 'left') { selected = this.startDate.clone(); minDate = this.minDate; } else if (side == 'right') { selected = this.endDate.clone(); minDate = this.startDate; //Preserve the time already selected var timeSelector = this.container.find('.calendar.right .calendar-time div'); if (timeSelector.html() != '') { selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour()); selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute()); selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second()); if (!this.timePicker24Hour) { var ampm = timeSelector.find('.ampmselect option:selected').val(); if (ampm === 'PM' && selected.hour() < 12) selected.hour(selected.hour() + 12); if (ampm === 'AM' && selected.hour() === 12) selected.hour(0); } } if (selected.isBefore(this.startDate)) selected = this.startDate.clone(); if (maxDate && selected.isAfter(maxDate)) selected = maxDate.clone(); } // // hours // html = ' '; // // minutes // html += ': '; // // seconds // if (this.timePickerSeconds) { html += ': '; } // // AM/PM // if (!this.timePicker24Hour) { html += ''; } this.container.find('.calendar.' + side + ' .calendar-time div').html(html); }, updateFormInputs: function() { //ignore mouse movements while an above-calendar text input has focus if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) return; this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format)); if (this.endDate) this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format)); if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) { this.container.find('button.applyBtn').removeAttr('disabled'); } else { this.container.find('button.applyBtn').attr('disabled', 'disabled'); } }, move: function() { var parentOffset = { top: 0, left: 0 }, containerTop; var parentRightEdge = $(window).width(); if (!this.parentEl.is('body')) { parentOffset = { top: this.parentEl.offset().top - this.parentEl.scrollTop(), left: this.parentEl.offset().left - this.parentEl.scrollLeft() }; parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left; } if (this.drops == 'up') containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top; else containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top; this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup'); if (this.opens == 'left') { this.container.css({ top: containerTop, right: parentRightEdge - this.element.offset().left - this.element.outerWidth(), left: 'auto' }); if (this.container.offset().left < 0) { this.container.css({ right: 'auto', left: 9 }); } } else if (this.opens == 'center') { this.container.css({ top: containerTop, left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2 - this.container.outerWidth() / 2, right: 'auto' }); if (this.container.offset().left < 0) { this.container.css({ right: 'auto', left: 9 }); } } else { this.container.css({ top: containerTop, left: this.element.offset().left - parentOffset.left, right: 'auto' }); if (this.container.offset().left + this.container.outerWidth() > $(window).width()) { this.container.css({ left: 'auto', right: 0 }); } } }, show: function(e) { if (this.isShowing) return; // Create a click proxy that is private to this instance of datepicker, for unbinding this._outsideClickProxy = $.proxy(function(e) { this.outsideClick(e); }, this); // Bind global datepicker mousedown for hiding and $(document) .on('mousedown.daterangepicker', this._outsideClickProxy) // also support mobile devices .on('touchend.daterangepicker', this._outsideClickProxy) // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy) // and also close when focus changes to outside the picker (eg. tabbing between controls) .on('focusin.daterangepicker', this._outsideClickProxy); // Reposition the picker if the window is resized while it's open $(window).on('resize.daterangepicker', $.proxy(function(e) { this.move(e); }, this)); this.oldStartDate = this.startDate.clone(); this.oldEndDate = this.endDate.clone(); this.previousRightTime = this.endDate.clone(); this.updateView(); this.container.show(); this.move(); this.element.trigger('show.daterangepicker', this); this.isShowing = true; }, hide: function(e) { if (!this.isShowing) return; //incomplete date selection, revert to last values if (!this.endDate) { this.startDate = this.oldStartDate.clone(); this.endDate = this.oldEndDate.clone(); } //if a new date range was selected, invoke the user callback function if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate)) this.callback(this.startDate, this.endDate, this.chosenLabel); //if picker is attached to a text input, update it this.updateElement(); $(document).off('.daterangepicker'); $(window).off('.daterangepicker'); this.container.hide(); this.element.trigger('hide.daterangepicker', this); this.isShowing = false; }, toggle: function(e) { if (this.isShowing) { this.hide(); } else { this.show(); } }, outsideClick: function(e) { var target = $(e.target); // if the page is clicked anywhere except within the daterangerpicker/button // itself then call this.hide() if ( // ie modal dialog fix e.type == "focusin" || target.closest(this.element).length || target.closest(this.container).length || target.closest('.calendar-table').length ) return; this.hide(); this.element.trigger('outsideClick.daterangepicker', this); }, showCalendars: function() { this.container.addClass('show-calendar'); this.move(); this.element.trigger('showCalendar.daterangepicker', this); }, hideCalendars: function() { this.container.removeClass('show-calendar'); this.element.trigger('hideCalendar.daterangepicker', this); }, hoverRange: function(e) { //ignore mouse movements while an above-calendar text input has focus if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) return; var label = e.target.getAttribute('data-range-key'); if (label == this.locale.customRangeLabel) { this.updateView(); } else { var dates = this.ranges[label]; this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format)); this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format)); } }, clickRange: function(e) { var label = e.target.getAttribute('data-range-key'); this.chosenLabel = label; if (label == this.locale.customRangeLabel) { this.showCalendars(); } else { var dates = this.ranges[label]; this.startDate = dates[0]; this.endDate = dates[1]; if (!this.timePicker) { this.startDate.startOf('day'); this.endDate.endOf('day'); } if (!this.alwaysShowCalendars) this.hideCalendars(); this.clickApply(); } }, clickPrev: function(e) { var cal = $(e.target).parents('.calendar'); if (cal.hasClass('left')) { this.leftCalendar.month.subtract(1, 'month'); if (this.linkedCalendars) this.rightCalendar.month.subtract(1, 'month'); } else { this.rightCalendar.month.subtract(1, 'month'); } this.updateCalendars(); }, clickNext: function(e) { var cal = $(e.target).parents('.calendar'); if (cal.hasClass('left')) { this.leftCalendar.month.add(1, 'month'); } else { this.rightCalendar.month.add(1, 'month'); if (this.linkedCalendars) this.leftCalendar.month.add(1, 'month'); } this.updateCalendars(); }, hoverDate: function(e) { //ignore mouse movements while an above-calendar text input has focus //if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) // return; //ignore dates that can't be selected if (!$(e.target).hasClass('available')) return; //have the text inputs above calendars reflect the date being hovered over var title = $(e.target).attr('data-title'); var row = title.substr(1, 1); var col = title.substr(3, 1); var cal = $(e.target).parents('.calendar'); var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(":focus")) { this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format)); } else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(":focus")) { this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format)); } //highlight the dates between the start date and the date being hovered as a potential end date var leftCalendar = this.leftCalendar; var rightCalendar = this.rightCalendar; var startDate = this.startDate; if (!this.endDate) { this.container.find('.calendar tbody td').each(function(index, el) { //skip week numbers, only look at dates if ($(el).hasClass('week')) return; var title = $(el).attr('data-title'); var row = title.substr(1, 1); var col = title.substr(3, 1); var cal = $(el).parents('.calendar'); var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col]; if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) { $(el).addClass('in-range'); } else { $(el).removeClass('in-range'); } }); } }, clickDate: function(e) { if (!$(e.target).hasClass('available')) return; var title = $(e.target).attr('data-title'); var row = title.substr(1, 1); var col = title.substr(3, 1); var cal = $(e.target).parents('.calendar'); var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; // // this function needs to do a few things: // * alternate between selecting a start and end date for the range, // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date // * if autoapply is enabled, and an end date was chosen, apply the selection // * if single date picker mode, and time picker isn't enabled, apply the selection immediately // * if one of the inputs above the calendars was focused, cancel that manual input // if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start if (this.timePicker) { var hour = parseInt(this.container.find('.left .hourselect').val(), 10); if (!this.timePicker24Hour) { var ampm = this.container.find('.left .ampmselect').val(); if (ampm === 'PM' && hour < 12) hour += 12; if (ampm === 'AM' && hour === 12) hour = 0; } var minute = parseInt(this.container.find('.left .minuteselect').val(), 10); var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; date = date.clone().hour(hour).minute(minute).second(second); } this.endDate = null; this.setStartDate(date.clone()); } else if (!this.endDate && date.isBefore(this.startDate)) { //special case: clicking the same date for start/end, //but the time of the end date is before the start date this.setEndDate(this.startDate.clone()); } else { // picking end if (this.timePicker) { var hour = parseInt(this.container.find('.right .hourselect').val(), 10); if (!this.timePicker24Hour) { var ampm = this.container.find('.right .ampmselect').val(); if (ampm === 'PM' && hour < 12) hour += 12; if (ampm === 'AM' && hour === 12) hour = 0; } var minute = parseInt(this.container.find('.right .minuteselect').val(), 10); var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; date = date.clone().hour(hour).minute(minute).second(second); } this.setEndDate(date.clone()); if (this.autoApply) { this.calculateChosenLabel(); this.clickApply(); } } if (this.singleDatePicker) { this.setEndDate(this.startDate); if (!this.timePicker) this.clickApply(); } this.updateView(); //This is to cancel the blur event handler if the mouse was in one of the inputs e.stopPropagation(); }, calculateChosenLabel: function () { var customRange = true; var i = 0; for (var range in this.ranges) { if (this.timePicker) { var format = this.timePickerSeconds ? "YYYY-MM-DD hh:mm:ss" : "YYYY-MM-DD hh:mm"; //ignore times when comparing dates if time picker seconds is not enabled if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) { customRange = false; this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); break; } } else { //ignore times when comparing dates if time picker is not enabled if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) { customRange = false; this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); break; } } i++; } if (customRange) { if (this.showCustomRangeLabel) { this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html(); } else { this.chosenLabel = null; } this.showCalendars(); } }, clickApply: function(e) { this.hide(); this.element.trigger('apply.daterangepicker', this); }, clickCancel: function(e) { this.startDate = this.oldStartDate; this.endDate = this.oldEndDate; this.hide(); this.element.trigger('cancel.daterangepicker', this); }, monthOrYearChanged: function(e) { var isLeft = $(e.target).closest('.calendar').hasClass('left'), leftOrRight = isLeft ? 'left' : 'right', cal = this.container.find('.calendar.'+leftOrRight); // Month must be Number for new moment versions var month = parseInt(cal.find('.monthselect').val(), 10); var year = cal.find('.yearselect').val(); if (!isLeft) { if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) { month = this.startDate.month(); year = this.startDate.year(); } } if (this.minDate) { if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) { month = this.minDate.month(); year = this.minDate.year(); } } if (this.maxDate) { if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) { month = this.maxDate.month(); year = this.maxDate.year(); } } if (isLeft) { this.leftCalendar.month.month(month).year(year); if (this.linkedCalendars) this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month'); } else { this.rightCalendar.month.month(month).year(year); if (this.linkedCalendars) this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month'); } this.updateCalendars(); }, timeChanged: function(e) { var cal = $(e.target).closest('.calendar'), isLeft = cal.hasClass('left'); var hour = parseInt(cal.find('.hourselect').val(), 10); var minute = parseInt(cal.find('.minuteselect').val(), 10); var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0; if (!this.timePicker24Hour) { var ampm = cal.find('.ampmselect').val(); if (ampm === 'PM' && hour < 12) hour += 12; if (ampm === 'AM' && hour === 12) hour = 0; } if (isLeft) { var start = this.startDate.clone(); start.hour(hour); start.minute(minute); start.second(second); this.setStartDate(start); if (this.singleDatePicker) { this.endDate = this.startDate.clone(); } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) { this.setEndDate(start.clone()); } } else if (this.endDate) { var end = this.endDate.clone(); end.hour(hour); end.minute(minute); end.second(second); this.setEndDate(end); } //update the calendars so all clickable dates reflect the new time component this.updateCalendars(); //update the form inputs above the calendars with the new time this.updateFormInputs(); //re-render the time pickers because changing one selection can affect what's enabled in another this.renderTimePicker('left'); this.renderTimePicker('right'); }, formInputsChanged: function(e) { var isRight = $(e.target).closest('.calendar').hasClass('right'); var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format); var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format); if (start.isValid() && end.isValid()) { if (isRight && end.isBefore(start)) start = end.clone(); this.setStartDate(start); this.setEndDate(end); if (isRight) { this.container.find('input[name="daterangepicker_start"]').val(this.startDate.format(this.locale.format)); } else { this.container.find('input[name="daterangepicker_end"]').val(this.endDate.format(this.locale.format)); } } this.updateView(); }, formInputsFocused: function(e) { // Highlight the focused input this.container.find('input[name="daterangepicker_start"], input[name="daterangepicker_end"]').removeClass('active'); $(e.target).addClass('active'); // Set the state such that if the user goes back to using a mouse, // the calendars are aware we're selecting the end of the range, not // the start. This allows someone to edit the end of a date range without // re-selecting the beginning, by clicking on the end date input then // using the calendar. var isRight = $(e.target).closest('.calendar').hasClass('right'); if (isRight) { this.endDate = null; this.setStartDate(this.startDate.clone()); this.updateView(); } }, formInputsBlurred: function(e) { // this function has one purpose right now: if you tab from the first // text input to the second in the UI, the endDate is nulled so that // you can click another, but if you tab out without clicking anything // or changing the input value, the old endDate should be retained if (!this.endDate) { var val = this.container.find('input[name="daterangepicker_end"]').val(); var end = moment(val, this.locale.format); if (end.isValid()) { this.setEndDate(end); this.updateView(); } } }, formInputsKeydown: function(e) { // This function ensures that if the 'enter' key was pressed in the input, then the calendars // are updated with the startDate and endDate. // This behaviour is automatic in Chrome/Firefox/Edge but not in IE 11 hence why this exists. // Other browsers and versions of IE are untested and the behaviour is unknown. if (e.keyCode === 13) { // Prevent the calendar from being updated twice on Chrome/Firefox/Edge e.preventDefault(); this.formInputsChanged(e); } }, elementChanged: function() { if (!this.element.is('input')) return; if (!this.element.val().length) return; var dateString = this.element.val().split(this.locale.separator), start = null, end = null; if (dateString.length === 2) { start = moment(dateString[0], this.locale.format); end = moment(dateString[1], this.locale.format); } if (this.singleDatePicker || start === null || end === null) { start = moment(this.element.val(), this.locale.format); end = start; } if (!start.isValid() || !end.isValid()) return; this.setStartDate(start); this.setEndDate(end); this.updateView(); }, keydown: function(e) { //hide on tab or enter if ((e.keyCode === 9) || (e.keyCode === 13)) { this.hide(); } //hide on esc and prevent propagation if (e.keyCode === 27) { e.preventDefault(); e.stopPropagation(); this.hide(); } }, updateElement: function() { if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); this.element.trigger('change'); } else if (this.element.is('input') && this.autoUpdateInput) { this.element.val(this.startDate.format(this.locale.format)); this.element.trigger('change'); } }, remove: function() { this.container.remove(); this.element.off('.daterangepicker'); this.element.removeData(); } }; $.fn.daterangepicker = function(options, callback) { var implementOptions = $.extend(true, {}, $.fn.daterangepicker.defaultOptions, options); this.each(function() { var el = $(this); if (el.data('daterangepicker')) el.data('daterangepicker').remove(); el.data('daterangepicker', new DateRangePicker(el, implementOptions, callback)); }); return this; }; return DateRangePicker; })); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/bower_components/fastclick/fastclick.js ================================================ ;(function () { 'use strict'; /** * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs. * * @codingstandard ftlabs-jsv2 * @copyright The Financial Times Limited [All Rights Reserved] * @license MIT License (see LICENSE.txt) */ /*jslint browser:true, node:true*/ /*global define, Event, Node*/ /** * Instantiate fast-clicking listeners on the specified layer. * * @constructor * @param {Element} layer The layer to listen on * @param {Object} [options={}] The options to override the defaults */ function FastClick(layer, options) { var oldOnClick; options = options || {}; /** * Whether a click is currently being tracked. * * @type boolean */ this.trackingClick = false; /** * Timestamp for when click tracking started. * * @type number */ this.trackingClickStart = 0; /** * The element being tracked for a click. * * @type EventTarget */ this.targetElement = null; /** * X-coordinate of touch start event. * * @type number */ this.touchStartX = 0; /** * Y-coordinate of touch start event. * * @type number */ this.touchStartY = 0; /** * ID of the last touch, retrieved from Touch.identifier. * * @type number */ this.lastTouchIdentifier = 0; /** * Touchmove boundary, beyond which a click will be cancelled. * * @type number */ this.touchBoundary = options.touchBoundary || 10; /** * The FastClick layer. * * @type Element */ this.layer = layer; /** * The minimum time between tap(touchstart and touchend) events * * @type number */ this.tapDelay = options.tapDelay || 200; /** * The maximum time for a tap * * @type number */ this.tapTimeout = options.tapTimeout || 700; if (FastClick.notNeeded(layer)) { return; } // Some old versions of Android don't have Function.prototype.bind function bind(method, context) { return function() { return method.apply(context, arguments); }; } var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel']; var context = this; for (var i = 0, l = methods.length; i < l; i++) { context[methods[i]] = bind(context[methods[i]], context); } // Set up event handlers as required if (deviceIsAndroid) { layer.addEventListener('mouseover', this.onMouse, true); layer.addEventListener('mousedown', this.onMouse, true); layer.addEventListener('mouseup', this.onMouse, true); } layer.addEventListener('click', this.onClick, true); layer.addEventListener('touchstart', this.onTouchStart, false); layer.addEventListener('touchmove', this.onTouchMove, false); layer.addEventListener('touchend', this.onTouchEnd, false); layer.addEventListener('touchcancel', this.onTouchCancel, false); // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2) // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick // layer when they are cancelled. if (!Event.prototype.stopImmediatePropagation) { layer.removeEventListener = function(type, callback, capture) { var rmv = Node.prototype.removeEventListener; if (type === 'click') { rmv.call(layer, type, callback.hijacked || callback, capture); } else { rmv.call(layer, type, callback, capture); } }; layer.addEventListener = function(type, callback, capture) { var adv = Node.prototype.addEventListener; if (type === 'click') { adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) { if (!event.propagationStopped) { callback(event); } }), capture); } else { adv.call(layer, type, callback, capture); } }; } // If a handler is already declared in the element's onclick attribute, it will be fired before // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and // adding it as listener. if (typeof layer.onclick === 'function') { // Android browser on at least 3.2 requires a new reference to the function in layer.onclick // - the old one won't work if passed to addEventListener directly. oldOnClick = layer.onclick; layer.addEventListener('click', function(event) { oldOnClick(event); }, false); layer.onclick = null; } } /** * Windows Phone 8.1 fakes user agent string to look like Android and iPhone. * * @type boolean */ var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0; /** * Android requires exceptions. * * @type boolean */ var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone; /** * iOS requires exceptions. * * @type boolean */ var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone; /** * iOS 4 requires an exception for select elements. * * @type boolean */ var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent); /** * iOS 6.0-7.* requires the target element to be manually derived * * @type boolean */ var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent); /** * BlackBerry requires exceptions. * * @type boolean */ var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0; /** * Determine whether a given element requires a native click. * * @param {EventTarget|Element} target Target DOM element * @returns {boolean} Returns true if the element needs a native click */ FastClick.prototype.needsClick = function(target) { switch (target.nodeName.toLowerCase()) { // Don't send a synthetic click to disabled inputs (issue #62) case 'button': case 'select': case 'textarea': if (target.disabled) { return true; } break; case 'input': // File inputs need real clicks on iOS 6 due to a browser bug (issue #68) if ((deviceIsIOS && target.type === 'file') || target.disabled) { return true; } break; case 'label': case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames case 'video': return true; } return (/\bneedsclick\b/).test(target.className); }; /** * Determine whether a given element requires a call to focus to simulate click into element. * * @param {EventTarget|Element} target Target DOM element * @returns {boolean} Returns true if the element requires a call to focus to simulate native click. */ FastClick.prototype.needsFocus = function(target) { switch (target.nodeName.toLowerCase()) { case 'textarea': return true; case 'select': return !deviceIsAndroid; case 'input': switch (target.type) { case 'button': case 'checkbox': case 'file': case 'image': case 'radio': case 'submit': return false; } // No point in attempting to focus disabled inputs return !target.disabled && !target.readOnly; default: return (/\bneedsfocus\b/).test(target.className); } }; /** * Send a click event to the specified element. * * @param {EventTarget|Element} targetElement * @param {Event} event */ FastClick.prototype.sendClick = function(targetElement, event) { var clickEvent, touch; // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24) if (document.activeElement && document.activeElement !== targetElement) { document.activeElement.blur(); } touch = event.changedTouches[0]; // Synthesise a click event, with an extra attribute so it can be tracked clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); clickEvent.forwardedTouchEvent = true; targetElement.dispatchEvent(clickEvent); }; FastClick.prototype.determineEventType = function(targetElement) { //Issue #159: Android Chrome Select Box does not open with a synthetic click event if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') { return 'mousedown'; } return 'click'; }; /** * @param {EventTarget|Element} targetElement */ FastClick.prototype.focus = function(targetElement) { var length; // Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724. if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') { length = targetElement.value.length; targetElement.setSelectionRange(length, length); } else { targetElement.focus(); } }; /** * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it. * * @param {EventTarget|Element} targetElement */ FastClick.prototype.updateScrollParent = function(targetElement) { var scrollParent, parentElement; scrollParent = targetElement.fastClickScrollParent; // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the // target element was moved to another parent. if (!scrollParent || !scrollParent.contains(targetElement)) { parentElement = targetElement; do { if (parentElement.scrollHeight > parentElement.offsetHeight) { scrollParent = parentElement; targetElement.fastClickScrollParent = parentElement; break; } parentElement = parentElement.parentElement; } while (parentElement); } // Always update the scroll top tracker if possible. if (scrollParent) { scrollParent.fastClickLastScrollTop = scrollParent.scrollTop; } }; /** * @param {EventTarget} targetElement * @returns {Element|EventTarget} */ FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) { // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node. if (eventTarget.nodeType === Node.TEXT_NODE) { return eventTarget.parentNode; } return eventTarget; }; /** * On touch start, record the position and scroll offset. * * @param {Event} event * @returns {boolean} */ FastClick.prototype.onTouchStart = function(event) { var targetElement, touch, selection; // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111). if (event.targetTouches.length > 1) { return true; } targetElement = this.getTargetElementFromEventTarget(event.target); touch = event.targetTouches[0]; if (deviceIsIOS) { // Only trusted events will deselect text on iOS (issue #49) selection = window.getSelection(); if (selection.rangeCount && !selection.isCollapsed) { return true; } if (!deviceIsIOS4) { // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23): // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched // with the same identifier as the touch event that previously triggered the click that triggered the alert. // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform. // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string, // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long, // random integers, it's safe to to continue if the identifier is 0 here. if (touch.identifier && touch.identifier === this.lastTouchIdentifier) { event.preventDefault(); return false; } this.lastTouchIdentifier = touch.identifier; // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and: // 1) the user does a fling scroll on the scrollable layer // 2) the user stops the fling scroll with another tap // then the event.target of the last 'touchend' event will be the element that was under the user's finger // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42). this.updateScrollParent(targetElement); } } this.trackingClick = true; this.trackingClickStart = event.timeStamp; this.targetElement = targetElement; this.touchStartX = touch.pageX; this.touchStartY = touch.pageY; // Prevent phantom clicks on fast double-tap (issue #36) if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { event.preventDefault(); } return true; }; /** * Based on a touchmove event object, check whether the touch has moved past a boundary since it started. * * @param {Event} event * @returns {boolean} */ FastClick.prototype.touchHasMoved = function(event) { var touch = event.changedTouches[0], boundary = this.touchBoundary; if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) { return true; } return false; }; /** * Update the last position. * * @param {Event} event * @returns {boolean} */ FastClick.prototype.onTouchMove = function(event) { if (!this.trackingClick) { return true; } // If the touch has moved, cancel the click tracking if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) { this.trackingClick = false; this.targetElement = null; } return true; }; /** * Attempt to find the labelled control for the given label element. * * @param {EventTarget|HTMLLabelElement} labelElement * @returns {Element|null} */ FastClick.prototype.findControl = function(labelElement) { // Fast path for newer browsers supporting the HTML5 control attribute if (labelElement.control !== undefined) { return labelElement.control; } // All browsers under test that support touch events also support the HTML5 htmlFor attribute if (labelElement.htmlFor) { return document.getElementById(labelElement.htmlFor); } // If no for attribute exists, attempt to retrieve the first labellable descendant element // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea'); }; /** * On touch end, determine whether to send a click event at once. * * @param {Event} event * @returns {boolean} */ FastClick.prototype.onTouchEnd = function(event) { var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement; if (!this.trackingClick) { return true; } // Prevent phantom clicks on fast double-tap (issue #36) if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { this.cancelNextClick = true; return true; } if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) { return true; } // Reset to prevent wrong click cancel on input (issue #156). this.cancelNextClick = false; this.lastClickTime = event.timeStamp; trackingClickStart = this.trackingClickStart; this.trackingClick = false; this.trackingClickStart = 0; // On some iOS devices, the targetElement supplied with the event is invalid if the layer // is performing a transition or scroll, and has to be re-detected manually. Note that // for this to function correctly, it must be called *after* the event target is checked! // See issue #57; also filed as rdar://13048589 . if (deviceIsIOSWithBadTarget) { touch = event.changedTouches[0]; // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement; targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent; } targetTagName = targetElement.tagName.toLowerCase(); if (targetTagName === 'label') { forElement = this.findControl(targetElement); if (forElement) { this.focus(targetElement); if (deviceIsAndroid) { return false; } targetElement = forElement; } } else if (this.needsFocus(targetElement)) { // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through. // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37). if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) { this.targetElement = null; return false; } this.focus(targetElement); this.sendClick(targetElement, event); // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open. // Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others) if (!deviceIsIOS || targetTagName !== 'select') { this.targetElement = null; event.preventDefault(); } return false; } if (deviceIsIOS && !deviceIsIOS4) { // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42). scrollParent = targetElement.fastClickScrollParent; if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) { return true; } } // Prevent the actual click from going though - unless the target node is marked as requiring // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted. if (!this.needsClick(targetElement)) { event.preventDefault(); this.sendClick(targetElement, event); } return false; }; /** * On touch cancel, stop tracking the click. * * @returns {void} */ FastClick.prototype.onTouchCancel = function() { this.trackingClick = false; this.targetElement = null; }; /** * Determine mouse events which should be permitted. * * @param {Event} event * @returns {boolean} */ FastClick.prototype.onMouse = function(event) { // If a target element was never set (because a touch event was never fired) allow the event if (!this.targetElement) { return true; } if (event.forwardedTouchEvent) { return true; } // Programmatically generated events targeting a specific element should be permitted if (!event.cancelable) { return true; } // Derive and check the target element to see whether the mouse event needs to be permitted; // unless explicitly enabled, prevent non-touch click events from triggering actions, // to prevent ghost/doubleclicks. if (!this.needsClick(this.targetElement) || this.cancelNextClick) { // Prevent any user-added listeners declared on FastClick element from being fired. if (event.stopImmediatePropagation) { event.stopImmediatePropagation(); } else { // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2) event.propagationStopped = true; } // Cancel the event event.stopPropagation(); event.preventDefault(); return false; } // If the mouse event is permitted, return true for the action to go through. return true; }; /** * On actual clicks, determine whether this is a touch-generated click, a click action occurring * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or * an actual click which should be permitted. * * @param {Event} event * @returns {boolean} */ FastClick.prototype.onClick = function(event) { var permitted; // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early. if (this.trackingClick) { this.targetElement = null; this.trackingClick = false; return true; } // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target. if (event.target.type === 'submit' && event.detail === 0) { return true; } permitted = this.onMouse(event); // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through. if (!permitted) { this.targetElement = null; } // If clicks are permitted, return true for the action to go through. return permitted; }; /** * Remove all FastClick's event listeners. * * @returns {void} */ FastClick.prototype.destroy = function() { var layer = this.layer; if (deviceIsAndroid) { layer.removeEventListener('mouseover', this.onMouse, true); layer.removeEventListener('mousedown', this.onMouse, true); layer.removeEventListener('mouseup', this.onMouse, true); } layer.removeEventListener('click', this.onClick, true); layer.removeEventListener('touchstart', this.onTouchStart, false); layer.removeEventListener('touchmove', this.onTouchMove, false); layer.removeEventListener('touchend', this.onTouchEnd, false); layer.removeEventListener('touchcancel', this.onTouchCancel, false); }; /** * Check whether FastClick is needed. * * @param {Element} layer The layer to listen on */ FastClick.notNeeded = function(layer) { var metaViewport; var chromeVersion; var blackberryVersion; var firefoxVersion; // Devices that don't support touch don't need FastClick if (typeof window.ontouchstart === 'undefined') { return true; } // Chrome version - zero for other browsers chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1]; if (chromeVersion) { if (deviceIsAndroid) { metaViewport = document.querySelector('meta[name=viewport]'); if (metaViewport) { // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89) if (metaViewport.content.indexOf('user-scalable=no') !== -1) { return true; } // Chrome 32 and above with width=device-width or less don't need FastClick if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) { return true; } } // Chrome desktop doesn't need FastClick (issue #15) } else { return true; } } if (deviceIsBlackBerry10) { blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/); // BlackBerry 10.3+ does not require Fastclick library. // https://github.com/ftlabs/fastclick/issues/251 if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) { metaViewport = document.querySelector('meta[name=viewport]'); if (metaViewport) { // user-scalable=no eliminates click delay. if (metaViewport.content.indexOf('user-scalable=no') !== -1) { return true; } // width=device-width (or less than device-width) eliminates click delay. if (document.documentElement.scrollWidth <= window.outerWidth) { return true; } } } } // IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97) if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') { return true; } // Firefox version - zero for other browsers firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1]; if (firefoxVersion >= 27) { // Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896 metaViewport = document.querySelector('meta[name=viewport]'); if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) { return true; } } // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') { return true; } return false; }; /** * Factory method for creating a FastClick object * * @param {Element} layer The layer to listen on * @param {Object} [options={}] The options to override the defaults */ FastClick.attach = function(layer, options) { return new FastClick(layer, options); }; if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) { // AMD. Register as an anonymous module. define(function() { return FastClick; }); } else if (typeof module !== 'undefined' && module.exports) { module.exports = FastClick.attach; module.exports.FastClick = FastClick; } else { window.FastClick = FastClick; } }()); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/adminlte/plugins/iCheck/square/blue.css ================================================ /* iCheck plugin Square skin, blue ----------------------------------- */ .icheckbox_square-blue, .iradio_square-blue { display: inline-block; *display: inline; vertical-align: middle; margin: 0; padding: 0; width: 22px; height: 22px; background: url(blue.png) no-repeat; border: none; cursor: pointer; } .icheckbox_square-blue { background-position: 0 0; } .icheckbox_square-blue.hover { background-position: -24px 0; } .icheckbox_square-blue.checked { background-position: -48px 0; } .icheckbox_square-blue.disabled { background-position: -72px 0; cursor: default; } .icheckbox_square-blue.checked.disabled { background-position: -96px 0; } .iradio_square-blue { background-position: -120px 0; } .iradio_square-blue.hover { background-position: -144px 0; } .iradio_square-blue.checked { background-position: -168px 0; } .iradio_square-blue.disabled { background-position: -192px 0; cursor: default; } .iradio_square-blue.checked.disabled { background-position: -216px 0; } /* Retina support */ @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (min-device-pixel-ratio: 1.5) { .icheckbox_square-blue, .iradio_square-blue { background-image: url(blue@2x.png); -webkit-background-size: 240px 24px; background-size: 240px 24px; } } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/common.1.js ================================================ $(function(){ // logout $("#logoutBtn").click(function(){ layer.confirm( I18n.logout_confirm , { icon: 3, title: I18n.system_tips , btn: [ I18n.system_ok, I18n.system_cancel ] }, function(index){ layer.close(index); $.post(base_url + "/logout", function(data, status) { if (data.code == "200") { layer.msg( I18n.logout_success ); setTimeout(function(){ window.location.href = base_url + "/"; }, 500); } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || I18n.logout_fail), icon: '2' }); } }); }); }); // slideToTop var slideToTop = $("
"); slideToTop.html(''); slideToTop.css({ position: 'fixed', bottom: '20px', right: '25px', width: '40px', height: '40px', color: '#eee', 'font-size': '', 'line-height': '40px', 'text-align': 'center', 'background-color': '#222d32', cursor: 'pointer', 'border-radius': '5px', 'z-index': '99999', opacity: '.7', 'display': 'none' }); slideToTop.on('mouseenter', function () { $(this).css('opacity', '1'); }); slideToTop.on('mouseout', function () { $(this).css('opacity', '.7'); }); $('.wrapper').append(slideToTop); $(window).scroll(function () { if ($(window).scrollTop() >= 150) { if (!$(slideToTop).is(':visible')) { $(slideToTop).fadeIn(500); } } else { $(slideToTop).fadeOut(500); } }); $(slideToTop).click(function () { $("html,body").animate({ // firefox ie not support body, chrome support body. but found that new version chrome not support body too. scrollTop: 0 }, 100); }); // left menu status v: js + server + cookie $('.sidebar-toggle').click(function(){ var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); // on=open,off=close if ('off' == xxljob_adminlte_settings) { xxljob_adminlte_settings = 'on'; } else { xxljob_adminlte_settings = 'off'; } $.cookie('xxljob_adminlte_settings', xxljob_adminlte_settings, { expires: 7 }); //$.cookie('the_cookie', '', { expires: -1 }); }); // left menu status v1: js + cookie /* var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings'); if (xxljob_adminlte_settings == 'off') { $('body').addClass('sidebar-collapse'); } */ // update pwd $('#updatePwd').on('click', function(){ $('#updatePwdModal').modal({backdrop: false, keyboard: false}).modal('show'); }); var updatePwdModalValidate = $("#updatePwdModal .form").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, rules : { password : { required : true , rangelength:[4,50] } }, messages : { password : { required : '请输入密码' , rangelength : "密码长度限制为4~50" } }, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { $.post(base_url + "/user/updatePwd", $("#updatePwdModal .form").serialize(), function(data, status) { if (data.code == 200) { $('#updatePwdModal').modal('hide'); layer.msg( I18n.change_pwd_suc_to_logout ); setTimeout(function(){ $.post(base_url + "/logout", function(data, status) { if (data.code == 200) { window.location.href = base_url + "/"; } else { layer.open({ icon: '2', content: (data.msg|| I18n.logout_fail) }); } }); }, 500); } else { layer.open({ icon: '2', content: (data.msg|| I18n.change_pwd + I18n.system_fail ) }); } }); } }); $("#updatePwdModal").on('hide.bs.modal', function () { $("#updatePwdModal .form")[0].reset(); updatePwdModalValidate.resetForm(); $("#updatePwdModal .form .form-group").removeClass("has-error"); }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/index.js ================================================ /** * Created by xuxueli on 17/4/24. */ $(function () { // filter Time var rangesConf = {}; rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')]; rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')]; rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')]; rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')]; rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')]; rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')]; $('#filterTime').daterangepicker({ autoApply:false, singleDatePicker:false, showDropdowns:false, // 是否显示年月选择条件 timePicker: true, // 是否显示小时和分钟选择条件 timePickerIncrement: 10, // 时间的增量,单位为分钟 timePicker24Hour : true, opens : 'left', //日期选择框的弹出位置 ranges: rangesConf, locale : { format: 'YYYY-MM-DD HH:mm:ss', separator : ' - ', customRangeLabel : I18n.daterangepicker_custom_name , applyLabel : I18n.system_ok , cancelLabel : I18n.system_cancel , fromLabel : I18n.daterangepicker_custom_starttime , toLabel : I18n.daterangepicker_custom_endtime , daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') , // '日', '一', '二', '三', '四', '五', '六' monthNames : I18n.daterangepicker_custom_monthnames.split(',') , // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' firstDay : 1 }, startDate: rangesConf[I18n.daterangepicker_ranges_recent_week][0] , endDate: rangesConf[I18n.daterangepicker_ranges_recent_week][1] }, function (start, end, label) { freshChartDate(start, end); }); freshChartDate(rangesConf[I18n.daterangepicker_ranges_recent_week][0], rangesConf[I18n.daterangepicker_ranges_recent_week][1]); /** * fresh Chart Date * * @param startDate * @param endDate */ function freshChartDate(startDate, endDate) { $.ajax({ type : 'POST', url : base_url + '/chartInfo', data : { 'startDate':startDate.format('YYYY-MM-DD HH:mm:ss'), 'endDate':endDate.format('YYYY-MM-DD HH:mm:ss') }, dataType : "json", success : function(data){ if (data.code == 200) { lineChartInit(data) pieChartInit(data); } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || I18n.job_dashboard_report_loaddata_fail ), icon: '2' }); } } }); } /** * line Chart Init */ function lineChartInit(data) { var option = { title: { text: I18n.job_dashboard_date_report }, tooltip : { trigger: 'axis', axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } } }, legend: { data:[I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running] }, toolbox: { feature: { /*saveAsImage: {}*/ } }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis : [ { type : 'category', boundaryGap : false, data : data.content.triggerDayList } ], yAxis : [ { type : 'value' } ], series : [ { name:I18n.joblog_status_suc, type:'line', stack: 'Total', areaStyle: {normal: {}}, data: data.content.triggerDayCountSucList }, { name:I18n.joblog_status_fail, type:'line', stack: 'Total', label: { normal: { show: true, position: 'top' } }, areaStyle: {normal: {}}, data: data.content.triggerDayCountFailList }, { name:I18n.joblog_status_running, type:'line', stack: 'Total', areaStyle: {normal: {}}, data: data.content.triggerDayCountRunningList } ], color:['#00A65A', '#c23632', '#F39C12'] }; var lineChart = echarts.init(document.getElementById('lineChart')); lineChart.setOption(option); } /** * pie Chart Init */ function pieChartInit(data) { var option = { title : { text: I18n.job_dashboard_rate_report , /*subtext: 'subtext',*/ x:'center' }, tooltip : { trigger: 'item', formatter: "{b} : {c} ({d}%)" }, legend: { orient: 'vertical', left: 'left', data: [I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running ] }, series : [ { //name: '分布比例', type: 'pie', radius : '55%', center: ['50%', '60%'], data:[ { name:I18n.joblog_status_suc, value:data.content.triggerCountSucTotal }, { name:I18n.joblog_status_fail, value:data.content.triggerCountFailTotal }, { name:I18n.joblog_status_running, value:data.content.triggerCountRunningTotal } ], itemStyle: { emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ], color:['#00A65A', '#c23632', '#F39C12'] }; var pieChart = echarts.init(document.getElementById('pieChart')); pieChart.setOption(option); } }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/jobcode.index.1.js ================================================ $(function() { // init code editor var codeEditor; function initIde(glueSource) { if (codeEditor == null) { codeEditor = CodeMirror(document.getElementById("ideWindow"), { mode : ideMode, lineNumbers : true, matchBrackets : true, value: glueSource }); } else { codeEditor.setValue(glueSource); } } initIde($("#version_now").val()); // code change $(".source_version").click(function(){ var sourceId = $(this).attr('version'); var temp = $( "#" + sourceId ).val(); //codeEditor.setValue(''); initIde(temp); }); // code source save $("#save").click(function() { $('#saveModal').modal({backdrop: false, keyboard: false}).modal('show'); }); $("#saveModal .ok").click(function() { var glueSource = codeEditor.getValue(); var glueRemark = $("#glueRemark").val(); if (!glueRemark) { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok], content: I18n.system_please_input + I18n.jobinfo_glue_remark , icon: '2' }); return; } if (glueRemark.length <4 || glueRemark.length > 100) { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: I18n.jobinfo_glue_remark_limit , icon: '2' }); return; } $.ajax({ type : 'POST', url : base_url + '/jobcode/save', data : { 'id' : id, 'glueSource' : glueSource, 'glueRemark' : glueRemark }, dataType : "json", success : function(data){ if (data.code == 200) { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: (I18n.system_save + I18n.system_success) , icon: '1', end: function(layero, index){ //$(window).unbind('beforeunload'); window.location.reload(); } }); } else { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: (data.msg || (I18n.system_save + I18n.system_fail) ), icon: '2' }); } } }); }); // before upload /*$(window).bind('beforeunload',function(){ return 'Glue尚未保存,确定离开Glue编辑器?'; });*/ }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/jobgroup.index.1.js ================================================ $(function() { // init date tables var jobGroupTable = $("#jobgroup_list").dataTable({ "deferRender": true, "processing" : true, "serverSide": true, "ajax": { url: base_url + "/jobgroup/pageList", type:"post", data : function ( d ) { var obj = {}; obj.appname = $('#appname').val(); obj.title = $('#title').val(); obj.start = d.start; obj.length = d.length; return obj; } }, "searching": false, "ordering": false, //"scrollX": true, // scroll x,close self-adaption "columns": [ { "data": 'id', "visible" : false }, { "data": 'appname', "visible" : true, "width":'30%' }, { "data": 'title', "visible" : true, "width":'30%' }, { "data": 'addressType', "width":'10%', "visible" : true, "render": function ( data, type, row ) { if (row.addressType == 0) { return I18n.jobgroup_field_addressType_0; } else { return I18n.jobgroup_field_addressType_1; } } }, { "data": 'registryList', "width":'15%', "visible" : true, "render": function ( data, type, row ) { return row.registryList ?'' + I18n.system_show +' ( ' + row.registryList.length+ ' )' :I18n.system_empty; } }, { "data": I18n.system_opt , "width":'15%', "render": function ( data, type, row ) { return function(){ // data tableData['key'+row.id] = row; // opt var html = '
\n' + ' \n' + ' \n' + ' \n' + '
'; return html; }; } } ], "language" : { "sProcessing" : I18n.dataTable_sProcessing , "sLengthMenu" : I18n.dataTable_sLengthMenu , "sZeroRecords" : I18n.dataTable_sZeroRecords , "sInfo" : I18n.dataTable_sInfo , "sInfoEmpty" : I18n.dataTable_sInfoEmpty , "sInfoFiltered" : I18n.dataTable_sInfoFiltered , "sInfoPostFix" : "", "sSearch" : I18n.dataTable_sSearch , "sUrl" : "", "sEmptyTable" : I18n.dataTable_sEmptyTable , "sLoadingRecords" : I18n.dataTable_sLoadingRecords , "sInfoThousands" : ",", "oPaginate" : { "sFirst" : I18n.dataTable_sFirst , "sPrevious" : I18n.dataTable_sPrevious , "sNext" : I18n.dataTable_sNext , "sLast" : I18n.dataTable_sLast }, "oAria" : { "sSortAscending" : I18n.dataTable_sSortAscending , "sSortDescending" : I18n.dataTable_sSortDescending } } }); // table data var tableData = {}; // search btn $('#searchBtn').on('click', function(){ jobGroupTable.fnDraw(); }); // job registryinfo $("#jobgroup_list").on('click', '.show_registryList',function() { var id = $(this).attr("_id"); var row = tableData['key'+id]; var html = '
'; if (row.registryList) { for (var index in row.registryList) { html += (parseInt(index)+1) + '. ' + row.registryList[index] + '
'; } } html += '
'; layer.open({ title: I18n.jobinfo_opt_registryinfo , btn: [ I18n.system_ok ], content: html }); }); // opt_del $("#jobgroup_list").on('click', '.opt_del',function() { var id = $(this).parents('ul').attr("_id"); layer.confirm( (I18n.system_ok + I18n.jobgroup_del + '?') , { icon: 3, title: I18n.system_tips , btn: [ I18n.system_ok, I18n.system_cancel ] }, function(index){ layer.close(index); $.ajax({ type : 'POST', url : base_url + '/jobgroup/remove', data : {"id":id}, dataType : "json", success : function(data){ if (data.code == 200) { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (I18n.jobgroup_del + I18n.system_success), icon: '1', end: function(layero, index){ jobGroupTable.fnDraw(); } }); } else { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: (data.msg || (I18n.jobgroup_del + I18n.system_fail)), icon: '2' }); } }, }); }); }); // jquery.validate “low letters start, limit contants、 letters、numbers and line-through.” jQuery.validator.addMethod("myValid01", function(value, element) { var length = value.length; var valid = /^[a-z][a-zA-Z0-9-]*$/; return this.optional(element) || valid.test(value); }, I18n.jobgroup_field_appname_limit ); $('.add').on('click', function(){ $('#addModal').modal({backdrop: false, keyboard: false}).modal('show'); }); var addModalValidate = $("#addModal .form").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, rules : { appname : { required : true, rangelength:[4,64], myValid01 : true }, title : { required : true, rangelength:[4, 12] } }, messages : { appname : { required : I18n.system_please_input+"AppName", rangelength: I18n.jobgroup_field_appname_length , myValid01: I18n.jobgroup_field_appname_limit }, title : { required : I18n.system_please_input + I18n.jobgroup_field_title , rangelength: I18n.jobgroup_field_title_length } }, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { $.post(base_url + "/jobgroup/save", $("#addModal .form").serialize(), function(data, status) { if (data.code == "200") { $('#addModal').modal('hide'); layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: I18n.system_add_suc , icon: '1', end: function(layero, index){ jobGroupTable.fnDraw(); } }); } else { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: (data.msg || I18n.system_add_fail ), icon: '2' }); } }); } }); $("#addModal").on('hide.bs.modal', function () { $("#addModal .form")[0].reset(); addModalValidate.resetForm(); $("#addModal .form .form-group").removeClass("has-error"); }); // addressType change $("#addModal input[name=addressType], #updateModal input[name=addressType]").click(function(){ var addressType = $(this).val(); var $addressList = $(this).parents("form").find("textarea[name=addressList]"); if (addressType == 0) { $addressList.css("background-color", "#eee"); // 自动注册 $addressList.attr("readonly","readonly"); $addressList.val(""); } else { $addressList.css("background-color", "white"); $addressList.removeAttr("readonly"); } }); // opt_edit $("#jobgroup_list").on('click', '.opt_edit',function() { var id = $(this).parents('ul').attr("_id"); var row = tableData['key'+id]; $("#updateModal .form input[name='id']").val( row.id ); $("#updateModal .form input[name='appname']").val( row.appname ); $("#updateModal .form input[name='title']").val( row.title ); // 注册方式 $("#updateModal .form input[name='addressType']").removeAttr('checked'); $("#updateModal .form input[name='addressType'][value='"+ row.addressType +"']").click(); // 机器地址 $("#updateModal .form textarea[name='addressList']").val( row.addressList ); $('#updateModal').modal({backdrop: false, keyboard: false}).modal('show'); }); var updateModalValidate = $("#updateModal .form").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, rules : { appname : { required : true, rangelength:[4,64], myValid01 : true }, title : { required : true, rangelength:[4, 12] } }, messages : { appname : { required : I18n.system_please_input+"AppName", rangelength: I18n.jobgroup_field_appname_length , myValid01: I18n.jobgroup_field_appname_limit }, title : { required : I18n.system_please_input + I18n.jobgroup_field_title , rangelength: I18n.jobgroup_field_title_length } }, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { $.post(base_url + "/jobgroup/update", $("#updateModal .form").serialize(), function(data, status) { if (data.code == "200") { $('#updateModal').modal('hide'); layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: I18n.system_update_suc , icon: '1', end: function(layero, index){ jobGroupTable.fnDraw(); } }); } else { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: (data.msg || I18n.system_update_fail ), icon: '2' }); } }); } }); $("#updateModal").on('hide.bs.modal', function () { $("#updateModal .form")[0].reset(); addModalValidate.resetForm(); $("#updateModal .form .form-group").removeClass("has-error"); }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/jobinfo.index.1.js ================================================ $(function() { // init date tables var jobTable = $("#job_list").dataTable({ "deferRender": true, "processing" : true, "serverSide": true, "ajax": { url: base_url + "/jobinfo/pageList", type:"post", data : function ( d ) { var obj = {}; obj.jobGroup = $('#jobGroup').val(); obj.triggerStatus = $('#triggerStatus').val(); obj.jobDesc = $('#jobDesc').val(); obj.executorHandler = $('#executorHandler').val(); obj.author = $('#author').val(); obj.start = d.start; obj.length = d.length; return obj; } }, "searching": false, "ordering": false, //"scrollX": true, // scroll x,close self-adaption "columns": [ { "data": 'id', "bSortable": false, "visible" : true, "width":'7%' }, { "data": 'jobGroup', "visible" : false, "render": function ( data, type, row ) { var groupMenu = $("#jobGroup").find("option"); for ( var index in $("#jobGroup").find("option")) { if ($(groupMenu[index]).attr('value') == data) { return $(groupMenu[index]).html(); } } return data; } }, { "data": 'jobDesc', "visible" : true, "width":'25%' }, { "data": 'scheduleType', "visible" : true, "width":'13%', "render": function ( data, type, row ) { if (row.scheduleConf) { return row.scheduleType + ':'+ row.scheduleConf; } else { return row.scheduleType; } } }, { "data": 'glueType', "width":'25%', "visible" : true, "render": function ( data, type, row ) { var glueTypeTitle = findGlueTypeTitle(row.glueType); if (row.executorHandler) { return glueTypeTitle +":" + row.executorHandler; } else { return glueTypeTitle; } } }, { "data": 'executorParam', "visible" : false}, { "data": 'addTime', "visible" : false, "render": function ( data, type, row ) { return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):""; } }, { "data": 'updateTime', "visible" : false, "render": function ( data, type, row ) { return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):""; } }, { "data": 'author', "visible" : true, "width":'10%'}, { "data": 'alarmEmail', "visible" : false}, { "data": 'triggerStatus', "width":'10%', "visible" : true, "render": function ( data, type, row ) { // status if (1 == data) { return 'RUNNING'; } else { return 'STOP'; } return data; } }, { "data": I18n.system_opt , "width":'10%', "render": function ( data, type, row ) { return function(){ // status var start_stop_div = ""; if (1 == row.triggerStatus ) { start_stop_div = '
  • '+ I18n.jobinfo_opt_stop +'
  • \n'; } else { start_stop_div = '
  • '+ I18n.jobinfo_opt_start +'
  • \n'; } // job_next_time_html var job_next_time_html = ''; if (row.scheduleType == 'CRON' || row.scheduleType == 'FIX_RATE') { job_next_time_html = '
  • ' + I18n.jobinfo_opt_next_time + '
  • \n'; } // log url var logHref = base_url +'/joblog?jobId='+ row.id; // code url var codeBtn = ""; if ('BEAN' != row.glueType) { var codeUrl = base_url +'/jobcode?jobId='+ row.id; codeBtn = '
  • GLUE IDE
  • \n'; codeBtn += '
  • \n'; } // data tableData['key'+row.id] = row; // opt var html = '
    \n' + ' \n' + ' \n' + ' \n' + '
    '; return html; }; } } ], "language" : { "sProcessing" : I18n.dataTable_sProcessing , "sLengthMenu" : I18n.dataTable_sLengthMenu , "sZeroRecords" : I18n.dataTable_sZeroRecords , "sInfo" : I18n.dataTable_sInfo , "sInfoEmpty" : I18n.dataTable_sInfoEmpty , "sInfoFiltered" : I18n.dataTable_sInfoFiltered , "sInfoPostFix" : "", "sSearch" : I18n.dataTable_sSearch , "sUrl" : "", "sEmptyTable" : I18n.dataTable_sEmptyTable , "sLoadingRecords" : I18n.dataTable_sLoadingRecords , "sInfoThousands" : ",", "oPaginate" : { "sFirst" : I18n.dataTable_sFirst , "sPrevious" : I18n.dataTable_sPrevious , "sNext" : I18n.dataTable_sNext , "sLast" : I18n.dataTable_sLast }, "oAria" : { "sSortAscending" : I18n.dataTable_sSortAscending , "sSortDescending" : I18n.dataTable_sSortDescending } } }); // table data var tableData = {}; // search btn $('#searchBtn').on('click', function(){ jobTable.fnDraw(); }); // jobGroup change $('#jobGroup').on('change', function(){ //reload var jobGroup = $('#jobGroup').val(); window.location.href = base_url + "/jobinfo?jobGroup=" + jobGroup; }); // job operate $("#job_list").on('click', '.job_operate',function() { var typeName; var url; var needFresh = false; var type = $(this).attr("_type"); if ("job_pause" == type) { typeName = I18n.jobinfo_opt_stop ; url = base_url + "/jobinfo/stop"; needFresh = true; } else if ("job_resume" == type) { typeName = I18n.jobinfo_opt_start ; url = base_url + "/jobinfo/start"; needFresh = true; } else if ("job_del" == type) { typeName = I18n.system_opt_del ; url = base_url + "/jobinfo/remove"; needFresh = true; } else { return; } var id = $(this).parents('ul').attr("_id"); layer.confirm( I18n.system_ok + typeName + '?', { icon: 3, title: I18n.system_tips , btn: [ I18n.system_ok, I18n.system_cancel ] }, function(index){ layer.close(index); $.ajax({ type : 'POST', url : url, data : { "id" : id }, dataType : "json", success : function(data){ if (data.code == 200) { layer.msg( typeName + I18n.system_success ); if (needFresh) { //window.location.reload(); jobTable.fnDraw(false); } } else { layer.msg( data.msg || typeName + I18n.system_fail ); } } }); }); }); // job trigger $("#job_list").on('click', '.job_trigger',function() { var id = $(this).parents('ul').attr("_id"); var row = tableData['key'+id]; $("#jobTriggerModal .form input[name='id']").val( row.id ); $("#jobTriggerModal .form textarea[name='executorParam']").val( row.executorParam ); $('#jobTriggerModal').modal({backdrop: false, keyboard: false}).modal('show'); }); $("#jobTriggerModal .ok").on('click',function() { $.ajax({ type : 'POST', url : base_url + "/jobinfo/trigger", data : { "id" : $("#jobTriggerModal .form input[name='id']").val(), "executorParam" : $("#jobTriggerModal .textarea[name='executorParam']").val(), "addressList" : $("#jobTriggerModal .textarea[name='addressList']").val() }, dataType : "json", success : function(data){ if (data.code == 200) { $('#jobTriggerModal').modal('hide'); layer.msg( I18n.jobinfo_opt_run + I18n.system_success ); } else { layer.msg( data.msg || I18n.jobinfo_opt_run + I18n.system_fail ); } } }); }); $("#jobTriggerModal").on('hide.bs.modal', function () { $("#jobTriggerModal .form")[0].reset(); }); // job registryinfo $("#job_list").on('click', '.job_registryinfo',function() { var id = $(this).parents('ul').attr("_id"); var row = tableData['key'+id]; var jobGroup = row.jobGroup; $.ajax({ type : 'POST', url : base_url + "/jobgroup/loadById", data : { "id" : jobGroup }, dataType : "json", success : function(data){ var html = '
    '; if (data.code == 200 && data.content.registryList) { for (var index in data.content.registryList) { html += (parseInt(index)+1) + '. ' + data.content.registryList[index] + '
    '; } } html += '
    '; layer.open({ title: I18n.jobinfo_opt_registryinfo , btn: [ I18n.system_ok ], content: html }); } }); }); // job_next_time $("#job_list").on('click', '.job_next_time',function() { var id = $(this).parents('ul').attr("_id"); var row = tableData['key'+id]; $.ajax({ type : 'POST', url : base_url + "/jobinfo/nextTriggerTime", data : { "scheduleType" : row.scheduleType, "scheduleConf" : row.scheduleConf }, dataType : "json", success : function(data){ if (data.code != 200) { layer.open({ title: I18n.jobinfo_opt_next_time , btn: [ I18n.system_ok ], content: data.msg }); } else { var html = '
    '; if (data.code == 200 && data.content) { for (var index in data.content) { html += '' + data.content[index] + '
    '; } } html += '
    '; layer.open({ title: I18n.jobinfo_opt_next_time , btn: [ I18n.system_ok ], content: html }); } } }); }); // add $(".add").click(function(){ // init-cronGen $("#addModal .form input[name='schedule_conf_CRON']").show().siblings().remove(); $("#addModal .form input[name='schedule_conf_CRON']").cronGen({}); // 》init scheduleType $("#updateModal .form select[name=scheduleType]").change(); // 》init glueType $("#updateModal .form select[name=glueType]").change(); $('#addModal').modal({backdrop: false, keyboard: false}).modal('show'); }); var addModalValidate = $("#addModal .form").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, rules : { jobDesc : { required : true, maxlength: 50 }, author : { required : true }/*, executorTimeout : { digits:true }, executorFailRetryCount : { digits:true }*/ }, messages : { jobDesc : { required : I18n.system_please_input + I18n.jobinfo_field_jobdesc }, author : { required : I18n.system_please_input + I18n.jobinfo_field_author }/*, executorTimeout : { digits: I18n.system_please_input + I18n.system_digits }, executorFailRetryCount : { digits: I18n.system_please_input + I18n.system_digits }*/ }, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { // process executorTimeout+executorFailRetryCount var executorTimeout = $("#addModal .form input[name='executorTimeout']").val(); if(!/^\d+$/.test(executorTimeout)) { executorTimeout = 0; } $("#addModal .form input[name='executorTimeout']").val(executorTimeout); var executorFailRetryCount = $("#addModal .form input[name='executorFailRetryCount']").val(); if(!/^\d+$/.test(executorFailRetryCount)) { executorFailRetryCount = 0; } $("#addModal .form input[name='executorFailRetryCount']").val(executorFailRetryCount); // process schedule_conf var scheduleType = $("#addModal .form select[name='scheduleType']").val(); var scheduleConf; if (scheduleType == 'CRON') { scheduleConf = $("#addModal .form input[name='cronGen_display']").val(); } else if (scheduleType == 'FIX_RATE') { scheduleConf = $("#addModal .form input[name='schedule_conf_FIX_RATE']").val(); } else if (scheduleType == 'FIX_DELAY') { scheduleConf = $("#addModal .form input[name='schedule_conf_FIX_DELAY']").val(); } $("#addModal .form input[name='scheduleConf']").val( scheduleConf ); $.post(base_url + "/jobinfo/add", $("#addModal .form").serialize(), function(data, status) { if (data.code == "200") { $('#addModal').modal('hide'); layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: I18n.system_add_suc , icon: '1', end: function(layero, index){ jobTable.fnDraw(); //window.location.reload(); } }); } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || I18n.system_add_fail), icon: '2' }); } }); } }); $("#addModal").on('hide.bs.modal', function () { addModalValidate.resetForm(); $("#addModal .form")[0].reset(); $("#addModal .form .form-group").removeClass("has-error"); $(".remote_panel").show(); // remote $("#addModal .form input[name='executorHandler']").removeAttr("readonly"); }); // scheduleType change $(".scheduleType").change(function(){ var scheduleType = $(this).val(); $(this).parents("form").find(".schedule_conf").hide(); $(this).parents("form").find(".schedule_conf_" + scheduleType).show(); }); // glueType change $(".glueType").change(function(){ // executorHandler var $executorHandler = $(this).parents("form").find("input[name='executorHandler']"); var glueType = $(this).val(); if ('BEAN' != glueType) { $executorHandler.val(""); $executorHandler.attr("readonly","readonly"); } else { $executorHandler.removeAttr("readonly"); } }); $("#addModal .glueType").change(function(){ // glueSource var glueType = $(this).val(); if ('GLUE_GROOVY'==glueType){ $("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_java").val() ); } else if ('GLUE_SHELL'==glueType){ $("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_shell").val() ); } else if ('GLUE_PYTHON'==glueType){ $("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_python").val() ); } else if ('GLUE_PHP'==glueType){ $("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_php").val() ); } else if ('GLUE_NODEJS'==glueType){ $("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_nodejs").val() ); } else if ('GLUE_POWERSHELL'==glueType){ $("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_powershell").val() ); } else { $("#addModal .form textarea[name='glueSource']").val(""); } }); // update $("#job_list").on('click', '.update',function() { var id = $(this).parents('ul').attr("_id"); var row = tableData['key'+id]; // fill base $("#updateModal .form input[name='id']").val( row.id ); $('#updateModal .form select[name=jobGroup] option[value='+ row.jobGroup +']').prop('selected', true); $("#updateModal .form input[name='jobDesc']").val( row.jobDesc ); $("#updateModal .form input[name='author']").val( row.author ); $("#updateModal .form input[name='alarmEmail']").val( row.alarmEmail ); // fill trigger $('#updateModal .form select[name=scheduleType] option[value='+ row.scheduleType +']').prop('selected', true); $("#updateModal .form input[name='scheduleConf']").val( row.scheduleConf ); if (row.scheduleType == 'CRON') { $("#updateModal .form input[name='schedule_conf_CRON']").val( row.scheduleConf ); } else if (row.scheduleType == 'FIX_RATE') { $("#updateModal .form input[name='schedule_conf_FIX_RATE']").val( row.scheduleConf ); } else if (row.scheduleType == 'FIX_DELAY') { $("#updateModal .form input[name='schedule_conf_FIX_DELAY']").val( row.scheduleConf ); } // 》init scheduleType $("#updateModal .form select[name=scheduleType]").change(); // fill job $('#updateModal .form select[name=glueType] option[value='+ row.glueType +']').prop('selected', true); $("#updateModal .form input[name='executorHandler']").val( row.executorHandler ); $("#updateModal .form textarea[name='executorParam']").val( row.executorParam ); // 》init glueType $("#updateModal .form select[name=glueType]").change(); // 》init-cronGen $("#updateModal .form input[name='schedule_conf_CRON']").show().siblings().remove(); $("#updateModal .form input[name='schedule_conf_CRON']").cronGen({}); // fill advanced $('#updateModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true); $("#updateModal .form input[name='childJobId']").val( row.childJobId ); $('#updateModal .form select[name=misfireStrategy] option[value='+ row.misfireStrategy +']').prop('selected', true); $('#updateModal .form select[name=executorBlockStrategy] option[value='+ row.executorBlockStrategy +']').prop('selected', true); $("#updateModal .form input[name='executorTimeout']").val( row.executorTimeout ); $("#updateModal .form input[name='executorFailRetryCount']").val( row.executorFailRetryCount ); // show $('#updateModal').modal({backdrop: false, keyboard: false}).modal('show'); }); var updateModalValidate = $("#updateModal .form").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, rules : { jobDesc : { required : true, maxlength: 50 }, author : { required : true } }, messages : { jobDesc : { required : I18n.system_please_input + I18n.jobinfo_field_jobdesc }, author : { required : I18n.system_please_input + I18n.jobinfo_field_author } }, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { // process executorTimeout + executorFailRetryCount var executorTimeout = $("#updateModal .form input[name='executorTimeout']").val(); if(!/^\d+$/.test(executorTimeout)) { executorTimeout = 0; } $("#updateModal .form input[name='executorTimeout']").val(executorTimeout); var executorFailRetryCount = $("#updateModal .form input[name='executorFailRetryCount']").val(); if(!/^\d+$/.test(executorFailRetryCount)) { executorFailRetryCount = 0; } $("#updateModal .form input[name='executorFailRetryCount']").val(executorFailRetryCount); // process schedule_conf var scheduleType = $("#updateModal .form select[name='scheduleType']").val(); var scheduleConf; if (scheduleType == 'CRON') { scheduleConf = $("#updateModal .form input[name='cronGen_display']").val(); } else if (scheduleType == 'FIX_RATE') { scheduleConf = $("#updateModal .form input[name='schedule_conf_FIX_RATE']").val(); } else if (scheduleType == 'FIX_DELAY') { scheduleConf = $("#updateModal .form input[name='schedule_conf_FIX_DELAY']").val(); } $("#updateModal .form input[name='scheduleConf']").val( scheduleConf ); // post $.post(base_url + "/jobinfo/update", $("#updateModal .form").serialize(), function(data, status) { if (data.code == "200") { $('#updateModal').modal('hide'); layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: I18n.system_update_suc , icon: '1', end: function(layero, index){ //window.location.reload(); jobTable.fnDraw(); } }); } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || I18n.system_update_fail ), icon: '2' }); } }); } }); $("#updateModal").on('hide.bs.modal', function () { updateModalValidate.resetForm(); $("#updateModal .form")[0].reset(); $("#updateModal .form .form-group").removeClass("has-error"); }); /** * find title by name, GlueType */ function findGlueTypeTitle(glueType) { var glueTypeTitle; $("#addModal .form select[name=glueType] option").each(function () { var name = $(this).val(); var title = $(this).text(); if (glueType == name) { glueTypeTitle = title; return false } }); return glueTypeTitle; } // job_copy $("#job_list").on('click', '.job_copy',function() { var id = $(this).parents('ul').attr("_id"); var row = tableData['key'+id]; // fill base $('#addModal .form select[name=jobGroup] option[value='+ row.jobGroup +']').prop('selected', true); $("#addModal .form input[name='jobDesc']").val( row.jobDesc ); $("#addModal .form input[name='author']").val( row.author ); $("#addModal .form input[name='alarmEmail']").val( row.alarmEmail ); // fill trigger $('#addModal .form select[name=scheduleType] option[value='+ row.scheduleType +']').prop('selected', true); $("#addModal .form input[name='scheduleConf']").val( row.scheduleConf ); if (row.scheduleType == 'CRON') { $("#addModal .form input[name='schedule_conf_CRON']").val( row.scheduleConf ); } else if (row.scheduleType == 'FIX_RATE') { $("#addModal .form input[name='schedule_conf_FIX_RATE']").val( row.scheduleConf ); } else if (row.scheduleType == 'FIX_DELAY') { $("#addModal .form input[name='schedule_conf_FIX_DELAY']").val( row.scheduleConf ); } // 》init scheduleType $("#addModal .form select[name=scheduleType]").change(); // fill job $('#addModal .form select[name=glueType] option[value='+ row.glueType +']').prop('selected', true); $("#addModal .form input[name='executorHandler']").val( row.executorHandler ); $("#addModal .form textarea[name='executorParam']").val( row.executorParam ); // 》init glueType $("#addModal .form select[name=glueType]").change(); // 》init-cronGen $("#addModal .form input[name='schedule_conf_CRON']").show().siblings().remove(); $("#addModal .form input[name='schedule_conf_CRON']").cronGen({}); // fill advanced $('#addModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true); $("#addModal .form input[name='childJobId']").val( row.childJobId ); $('#addModal .form select[name=misfireStrategy] option[value='+ row.misfireStrategy +']').prop('selected', true); $('#addModal .form select[name=executorBlockStrategy] option[value='+ row.executorBlockStrategy +']').prop('selected', true); $("#addModal .form input[name='executorTimeout']").val( row.executorTimeout ); $("#addModal .form input[name='executorFailRetryCount']").val( row.executorFailRetryCount ); // show $('#addModal').modal({backdrop: false, keyboard: false}).modal('show'); }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js ================================================ $(function() { // trigger fail, end if ( !(triggerCode == 200 || handleCode != 0) ) { $('#logConsoleRunning').hide(); $('#logConsole').append(''+ I18n.joblog_rolling_log_triggerfail +''); return; } // pull log var fromLineNum = 1; // [from, to], start as 1 var pullFailCount = 0; function pullLog() { // pullFailCount, max=20 if (pullFailCount++ > 20) { logRunStop(''+ I18n.joblog_rolling_log_failoften +''); return; } // load console.log("pullLog, fromLineNum:" + fromLineNum); $.ajax({ type : 'POST', async: false, // sync, make log ordered url : base_url + '/joblog/logDetailCat', data : { "logId":logId, "fromLineNum":fromLineNum }, dataType : "json", success : function(data){ if (data.code == 200) { if (!data.content) { console.log('pullLog fail'); return; } if (fromLineNum != data.content.fromLineNum) { console.log('pullLog fromLineNum not match'); return; } if (fromLineNum > data.content.toLineNum ) { console.log('pullLog already line-end'); // valid end if (data.content.end) { logRunStop('
    [Rolling Log Finish]'); return; } return; } // append content fromLineNum = data.content.toLineNum + 1; $('#logConsole').append(data.content.logContent); pullFailCount = 0; // scroll to bottom scrollTo(0, document.body.scrollHeight); // $('#logConsolePre').scrollTop( document.body.scrollHeight + 300 ); } else { console.log('pullLog fail:'+data.msg); } } }); } // pull first page pullLog(); // handler already callback, end if (handleCode > 0) { logRunStop('
    [Load Log Finish]'); return; } // round until end var logRun = setInterval(function () { pullLog() }, 3000); function logRunStop(content){ $('#logConsoleRunning').hide(); logRun = window.clearInterval(logRun); $('#logConsole').append(content); } }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.index.1.js ================================================ $(function() { // jobGroup change, job list init and select $("#jobGroup").on("change", function () { var jobGroup = $(this).children('option:selected').val(); $.ajax({ type : 'POST', async: false, // async, avoid js invoke pagelist before jobId data init url : base_url + '/joblog/getJobsByGroup', data : {"jobGroup":jobGroup}, dataType : "json", success : function(data){ if (data.code == 200) { $("#jobId").html( '' ); $.each(data.content, function (n, value) { $("#jobId").append(''); }); if ($("#jobId").attr("paramVal")){ $("#jobId").find("option[value='" + $("#jobId").attr("paramVal") + "']").attr("selected",true); } } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || I18n.system_api_error ), icon: '2' }); } }, }); }); if ($("#jobGroup").attr("paramVal")){ $("#jobGroup").find("option[value='" + $("#jobGroup").attr("paramVal") + "']").attr("selected",true); $("#jobGroup").change(); } // filter Time var rangesConf = {}; rangesConf[I18n.daterangepicker_ranges_recent_hour] = [moment().subtract(1, 'hours'), moment()]; rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')]; rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')]; rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')]; rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')]; rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')]; rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')]; $('#filterTime').daterangepicker({ autoApply:false, singleDatePicker:false, showDropdowns:false, // 是否显示年月选择条件 timePicker: true, // 是否显示小时和分钟选择条件 timePickerIncrement: 10, // 时间的增量,单位为分钟 timePicker24Hour : true, opens : 'left', //日期选择框的弹出位置 ranges: rangesConf, locale : { format: 'YYYY-MM-DD HH:mm:ss', separator : ' - ', customRangeLabel : I18n.daterangepicker_custom_name , applyLabel : I18n.system_ok , cancelLabel : I18n.system_cancel , fromLabel : I18n.daterangepicker_custom_starttime , toLabel : I18n.daterangepicker_custom_endtime , daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') , // '日', '一', '二', '三', '四', '五', '六' monthNames : I18n.daterangepicker_custom_monthnames.split(',') , // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' firstDay : 1 }, startDate: rangesConf[I18n.daterangepicker_ranges_today][0], endDate: rangesConf[I18n.daterangepicker_ranges_today][1] }); // init date tables var logTable = $("#joblog_list").dataTable({ "deferRender": true, "processing" : true, "serverSide": true, "ajax": { url: base_url + "/joblog/pageList" , type:"post", data : function ( d ) { var obj = {}; obj.jobGroup = $('#jobGroup').val(); obj.jobId = $('#jobId').val(); obj.logStatus = $('#logStatus').val(); obj.filterTime = $('#filterTime').val(); obj.start = d.start; obj.length = d.length; return obj; } }, "searching": false, "ordering": false, //"scrollX": false, "columns": [ { "data": 'jobId', "visible" : true, "width":'10%', "render": function ( data, type, row ) { var jobhandler = ''; if (row.executorHandler) { jobhandler = "
    JobHandler:" + row.executorHandler; } var temp = ''; temp += I18n.joblog_field_executorAddress + ':' + (row.executorAddress?row.executorAddress:''); temp += jobhandler; temp += '
    '+ I18n.jobinfo_field_executorparam +':' + row.executorParam; return ''+ row.jobId +''+ temp +''; } }, { "data": 'jobGroup', "visible" : false}, { "data": 'triggerTime', "width":'20%', "render": function ( data, type, row ) { return data?moment(data).format("YYYY-MM-DD HH:mm:ss"):""; } }, { "data": 'triggerCode', "width":'10%', "render": function ( data, type, row ) { var html = data; if (data == 200) { html = ''+ I18n.system_success +''; } else if (data == 500) { html = ''+ I18n.system_fail +''; } else if (data == 0) { html = ''; } return html; } }, { "data": 'triggerMsg', "width":'10%', "render": function ( data, type, row ) { return data?''+ I18n.system_show +''+ data +'':I18n.system_empty; } }, { "data": 'handleTime', "width":'20%', "render": function ( data, type, row ) { return data?moment(data).format("YYYY-MM-DD HH:mm:ss"):""; } }, { "data": 'handleCode', "width":'10%', "render": function ( data, type, row ) { var html = data; if (data == 200) { html = ''+ I18n.joblog_handleCode_200 +''; } else if (data == 500) { html = ''+ I18n.joblog_handleCode_500 +''; } else if (data == 502) { html = ''+ I18n.joblog_handleCode_502 +''; } else if (data == 0) { html = ''; } return html; } }, { "data": 'handleMsg', "width":'10%', "render": function ( data, type, row ) { return data?''+ I18n.system_show +''+ data +'':I18n.system_empty; } }, { "data": 'handleMsg' , "bSortable": false, "width":'10%', "render": function ( data, type, row ) { // better support expression or string, not function return function () { if (row.triggerCode == 200 || row.handleCode != 0){ /*var temp = ''+ I18n.joblog_rolling_log +''; if(row.handleCode == 0){ temp += '
    '+ I18n.joblog_kill_log +''; }*/ //return temp; var logKillDiv = ''; if(row.handleCode == 0){ logKillDiv = '
  • \n' + '
  • '+ I18n.joblog_kill_log +'
  • \n'; } var html = '
    \n' + ' \n' + ' \n' + ' \n' + '
    '; return html; } return null; } } } ], "language" : { "sProcessing" : I18n.dataTable_sProcessing , "sLengthMenu" : I18n.dataTable_sLengthMenu , "sZeroRecords" : I18n.dataTable_sZeroRecords , "sInfo" : I18n.dataTable_sInfo , "sInfoEmpty" : I18n.dataTable_sInfoEmpty , "sInfoFiltered" : I18n.dataTable_sInfoFiltered , "sInfoPostFix" : "", "sSearch" : I18n.dataTable_sSearch , "sUrl" : "", "sEmptyTable" : I18n.dataTable_sEmptyTable , "sLoadingRecords" : I18n.dataTable_sLoadingRecords , "sInfoThousands" : ",", "oPaginate" : { "sFirst" : I18n.dataTable_sFirst , "sPrevious" : I18n.dataTable_sPrevious , "sNext" : I18n.dataTable_sNext , "sLast" : I18n.dataTable_sLast }, "oAria" : { "sSortAscending" : I18n.dataTable_sSortAscending , "sSortDescending" : I18n.dataTable_sSortDescending } } }); logTable.on('xhr.dt',function(e, settings, json, xhr) { if (json.code && json.code != 200) { layer.msg( json.msg || I18n.system_api_error ); } }); // logTips alert $('#joblog_list').on('click', '.logTips', function(){ var msg = $(this).find('span').html(); ComAlertTec.show(msg); }); // search Btn $('#searchBtn').on('click', function(){ logTable.fnDraw(); }); // logDetail look $('#joblog_list').on('click', '.logDetail', function(){ var _id = $(this).attr('_id'); window.open(base_url + '/joblog/logDetailPage?id=' + _id); return; }); /** * log Kill */ $('#joblog_list').on('click', '.logKill', function(){ var _id = $(this).attr('_id'); layer.confirm( (I18n.system_ok + I18n.joblog_kill_log + '?'), { icon: 3, title: I18n.system_tips , btn: [ I18n.system_ok, I18n.system_cancel ] }, function(index){ layer.close(index); $.ajax({ type : 'POST', url : base_url + '/joblog/logKill', data : {"id":_id}, dataType : "json", success : function(data){ if (data.code == 200) { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: I18n.system_opt_suc , icon: '1', end: function(layero, index){ logTable.fnDraw(); } }); } else { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: (data.msg || I18n.system_opt_fail ), icon: '2' }); } }, }); }); }); /** * clear Log */ $('#clearLog').on('click', function(){ var jobGroup = $('#jobGroup').val(); var jobId = $('#jobId').val(); var jobGroupText = $("#jobGroup").find("option:selected").text(); var jobIdText = $("#jobId").find("option:selected").text(); $('#clearLogModal input[name=jobGroup]').val(jobGroup); $('#clearLogModal input[name=jobId]').val(jobId); $('#clearLogModal .jobGroupText').val(jobGroupText); $('#clearLogModal .jobIdText').val(jobIdText); $('#clearLogModal').modal('show'); }); $("#clearLogModal .ok").on('click', function(){ $.post(base_url + "/joblog/clearLog", $("#clearLogModal .form").serialize(), function(data, status) { if (data.code == "200") { $('#clearLogModal').modal('hide'); layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (I18n.joblog_clean_log + I18n.system_success) , icon: '1', end: function(layero, index){ logTable.fnDraw(); } }); } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || (I18n.joblog_clean_log + I18n.system_fail) ), icon: '2' }); } }); }); $("#clearLogModal").on('hide.bs.modal', function () { $("#clearLogModal .form")[0].reset(); }); }); // Com Alert by Tec theme var ComAlertTec = { html:function(){ var html = ''; return html; }, show:function(msg, callback){ // dom init if ($('#ComAlertTec').length == 0){ $('body').append(ComAlertTec.html()); } // init com alert $('#ComAlertTec .alert').html(msg); $('#ComAlertTec').modal('show'); $('#ComAlertTec .ok').click(function(){ $('#ComAlertTec').modal('hide'); if(typeof callback == 'function') { callback(); } }); } }; ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/login.1.js ================================================ $(function(){ // input iCheck $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' // optional }); // login Form Valid var loginFormValid = $("#loginForm").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, rules : { userName : { required : true , minlength: 4, maxlength: 18 }, password : { required : true , minlength: 4, maxlength: 18 } }, messages : { userName : { required : I18n.login_username_empty, minlength : I18n.login_username_lt_4 }, password : { required : I18n.login_password_empty , minlength : I18n.login_password_lt_4 /*,maxlength:"登录密码不应超过18位"*/ } }, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { $.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) { if (data.code == "200") { layer.msg( I18n.login_success ); setTimeout(function(){ window.location.href = base_url + "/"; }, 500); } else { layer.open({ title: I18n.system_tips, btn: [ I18n.system_ok ], content: (data.msg || I18n.login_fail ), icon: '2' }); } }); } }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/user.index.1.js ================================================ $(function() { // init date tables var userListTable = $("#user_list").dataTable({ "deferRender": true, "processing" : true, "serverSide": true, "ajax": { url: base_url + "/user/pageList", type:"post", data : function ( d ) { var obj = {}; obj.username = $('#username').val(); obj.role = $('#role').val(); obj.start = d.start; obj.length = d.length; return obj; } }, "searching": false, "ordering": false, //"scrollX": true, // scroll x,close self-adaption "columns": [ { "data": 'id', "visible" : false, "width":'10%' }, { "data": 'username', "visible" : true, "width":'20%' }, { "data": 'password', "visible" : false, "width":'20%', "render": function ( data, type, row ) { return '*********'; } }, { "data": 'role', "visible" : true, "width":'10%', "render": function ( data, type, row ) { if (data == 1) { return I18n.user_role_admin } else { return I18n.user_role_normal } } }, { "data": 'permission', "width":'10%', "visible" : false }, { "data": I18n.system_opt , "width":'15%', "render": function ( data, type, row ) { return function(){ // html tableData['key'+row.id] = row; var html = '

    '+ ' '+ ' '+ '

    '; return html; }; } } ], "language" : { "sProcessing" : I18n.dataTable_sProcessing , "sLengthMenu" : I18n.dataTable_sLengthMenu , "sZeroRecords" : I18n.dataTable_sZeroRecords , "sInfo" : I18n.dataTable_sInfo , "sInfoEmpty" : I18n.dataTable_sInfoEmpty , "sInfoFiltered" : I18n.dataTable_sInfoFiltered , "sInfoPostFix" : "", "sSearch" : I18n.dataTable_sSearch , "sUrl" : "", "sEmptyTable" : I18n.dataTable_sEmptyTable , "sLoadingRecords" : I18n.dataTable_sLoadingRecords , "sInfoThousands" : ",", "oPaginate" : { "sFirst" : I18n.dataTable_sFirst , "sPrevious" : I18n.dataTable_sPrevious , "sNext" : I18n.dataTable_sNext , "sLast" : I18n.dataTable_sLast }, "oAria" : { "sSortAscending" : I18n.dataTable_sSortAscending , "sSortDescending" : I18n.dataTable_sSortDescending } } }); // table data var tableData = {}; // search btn $('#searchBtn').on('click', function(){ userListTable.fnDraw(); }); // job operate $("#user_list").on('click', '.delete',function() { var id = $(this).parent('p').attr("id"); layer.confirm( I18n.system_ok + I18n.system_opt_del + '?', { icon: 3, title: I18n.system_tips , btn: [ I18n.system_ok, I18n.system_cancel ] }, function(index){ layer.close(index); $.ajax({ type : 'POST', url : base_url + "/user/remove", data : { "id" : id }, dataType : "json", success : function(data){ if (data.code == 200) { layer.msg( I18n.system_success ); userListTable.fnDraw(false); } else { layer.msg( data.msg || I18n.system_opt_del + I18n.system_fail ); } } }); }); }); // add role $("#addModal .form input[name=role]").change(function () { var role = $(this).val(); if (role == 1) { $("#addModal .form input[name=permission]").parents('.form-group').hide(); } else { $("#addModal .form input[name=permission]").parents('.form-group').show(); } $("#addModal .form input[name='permission']").prop("checked",false); }); jQuery.validator.addMethod("myValid01", function(value, element) { var length = value.length; var valid = /^[a-z][a-z0-9]*$/; return this.optional(element) || valid.test(value); }, I18n.user_username_valid ); // add $(".add").click(function(){ $('#addModal').modal({backdrop: false, keyboard: false}).modal('show'); }); var addModalValidate = $("#addModal .form").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, rules : { username : { required : true, rangelength:[4, 20], myValid01: true }, password : { required : true, rangelength:[4, 20] } }, messages : { username : { required : I18n.system_please_input + I18n.user_username, rangelength: I18n.system_lengh_limit + "[4-20]" }, password : { required : I18n.system_please_input + I18n.user_password, rangelength: I18n.system_lengh_limit + "[4-20]" } }, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { var permissionArr = []; $("#addModal .form input[name=permission]:checked").each(function(){ permissionArr.push($(this).val()); }); var paramData = { "username": $("#addModal .form input[name=username]").val(), "password": $("#addModal .form input[name=password]").val(), "role": $("#addModal .form input[name=role]:checked").val(), "permission": permissionArr.join(',') }; $.post(base_url + "/user/add", paramData, function(data, status) { if (data.code == "200") { $('#addModal').modal('hide'); layer.msg( I18n.system_add_suc ); userListTable.fnDraw(); } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || I18n.system_add_fail), icon: '2' }); } }); } }); $("#addModal").on('hide.bs.modal', function () { $("#addModal .form")[0].reset(); addModalValidate.resetForm(); $("#addModal .form .form-group").removeClass("has-error"); $(".remote_panel").show(); // remote $("#addModal .form input[name=permission]").parents('.form-group').show(); }); // update role $("#updateModal .form input[name=role]").change(function () { var role = $(this).val(); if (role == 1) { $("#updateModal .form input[name=permission]").parents('.form-group').hide(); } else { $("#updateModal .form input[name=permission]").parents('.form-group').show(); } $("#updateModal .form input[name='permission']").prop("checked",false); }); // update $("#user_list").on('click', '.update',function() { var id = $(this).parent('p').attr("id"); var row = tableData['key'+id]; // base data $("#updateModal .form input[name='id']").val( row.id ); $("#updateModal .form input[name='username']").val( row.username ); $("#updateModal .form input[name='password']").val( '' ); $("#updateModal .form input[name='role'][value='"+ row.role +"']").click(); var permissionArr = []; if (row.permission) { permissionArr = row.permission.split(","); } $("#updateModal .form input[name='permission']").each(function () { if($.inArray($(this).val(), permissionArr) > -1) { $(this).prop("checked",true); } else { $(this).prop("checked",false); } }); // show $('#updateModal').modal({backdrop: false, keyboard: false}).modal('show'); }); var updateModalValidate = $("#updateModal .form").validate({ errorElement : 'span', errorClass : 'help-block', focusInvalid : true, highlight : function(element) { $(element).closest('.form-group').addClass('has-error'); }, success : function(label) { label.closest('.form-group').removeClass('has-error'); label.remove(); }, errorPlacement : function(error, element) { element.parent('div').append(error); }, submitHandler : function(form) { var permissionArr =[]; $("#updateModal .form input[name=permission]:checked").each(function(){ permissionArr.push($(this).val()); }); var paramData = { "id": $("#updateModal .form input[name=id]").val(), "username": $("#updateModal .form input[name=username]").val(), "password": $("#updateModal .form input[name=password]").val(), "role": $("#updateModal .form input[name=role]:checked").val(), "permission": permissionArr.join(',') }; $.post(base_url + "/user/update", paramData, function(data, status) { if (data.code == "200") { $('#updateModal').modal('hide'); layer.msg( I18n.system_update_suc ); userListTable.fnDraw(); } else { layer.open({ title: I18n.system_tips , btn: [ I18n.system_ok ], content: (data.msg || I18n.system_update_fail), icon: '2' }); } }); } }); $("#updateModal").on('hide.bs.modal', function () { $("#updateModal .form")[0].reset(); updateModalValidate.resetForm(); $("#updateModal .form .form-group").removeClass("has-error"); $(".remote_panel").show(); // remote $("#updateModal .form input[name=permission]").parents('.form-group').show(); }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/addon/hint/anyword-hint.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; var WORD = /[\w$]+/, RANGE = 500; CodeMirror.registerHelper("hint", "anyword", function(editor, options) { var word = options && options.word || WORD; var range = options && options.range || RANGE; var cur = editor.getCursor(), curLine = editor.getLine(cur.line); var end = cur.ch, start = end; while (start && word.test(curLine.charAt(start - 1))) --start; var curWord = start != end && curLine.slice(start, end); var list = options && options.list || [], seen = {}; var re = new RegExp(word.source, "g"); for (var dir = -1; dir <= 1; dir += 2) { var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; for (; line != endLine; line += dir) { var text = editor.getLine(line), m; while (m = re.exec(text)) { if (line == cur.line && m[0] === curWord) continue; if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { seen[m[0]] = true; list.push(m[0]); } } } } return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/addon/hint/show-hint.css ================================================ .CodeMirror-hints { position: absolute; z-index: 10; overflow: hidden; list-style: none; margin: 0; padding: 2px; -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); box-shadow: 2px 3px 5px rgba(0,0,0,.2); border-radius: 3px; border: 1px solid silver; background: white; font-size: 90%; font-family: monospace; max-height: 20em; overflow-y: auto; } .CodeMirror-hint { margin: 0; padding: 0 4px; border-radius: 2px; white-space: pre; color: black; cursor: pointer; } li.CodeMirror-hint-active { background: #08f; color: white; } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/addon/hint/show-hint.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; var HINT_ELEMENT_CLASS = "CodeMirror-hint"; var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; // This is the old interface, kept around for now to stay // backwards-compatible. CodeMirror.showHint = function(cm, getHints, options) { if (!getHints) return cm.showHint(options); if (options && options.async) getHints.async = true; var newOpts = {hint: getHints}; if (options) for (var prop in options) newOpts[prop] = options[prop]; return cm.showHint(newOpts); }; CodeMirror.defineExtension("showHint", function(options) { options = parseOptions(this, this.getCursor("start"), options); var selections = this.listSelections() if (selections.length > 1) return; // By default, don't allow completion when something is selected. // A hint function can have a `supportsSelection` property to // indicate that it can handle selections. if (this.somethingSelected()) { if (!options.hint.supportsSelection) return; // Don't try with cross-line selections for (var i = 0; i < selections.length; i++) if (selections[i].head.line != selections[i].anchor.line) return; } if (this.state.completionActive) this.state.completionActive.close(); var completion = this.state.completionActive = new Completion(this, options); if (!completion.options.hint) return; CodeMirror.signal(this, "startCompletion", this); completion.update(true); }); function Completion(cm, options) { this.cm = cm; this.options = options; this.widget = null; this.debounce = 0; this.tick = 0; this.startPos = this.cm.getCursor("start"); this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; var self = this; cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); } var requestAnimationFrame = window.requestAnimationFrame || function(fn) { return setTimeout(fn, 1000/60); }; var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; Completion.prototype = { close: function() { if (!this.active()) return; this.cm.state.completionActive = null; this.tick = null; this.cm.off("cursorActivity", this.activityFunc); if (this.widget && this.data) CodeMirror.signal(this.data, "close"); if (this.widget) this.widget.close(); CodeMirror.signal(this.cm, "endCompletion", this.cm); }, active: function() { return this.cm.state.completionActive == this; }, pick: function(data, i) { var completion = data.list[i]; if (completion.hint) completion.hint(this.cm, data, completion); else this.cm.replaceRange(getText(completion), completion.from || data.from, completion.to || data.to, "complete"); CodeMirror.signal(data, "pick", completion); this.close(); }, cursorActivity: function() { if (this.debounce) { cancelAnimationFrame(this.debounce); this.debounce = 0; } var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || pos.ch < this.startPos.ch || this.cm.somethingSelected() || (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { this.close(); } else { var self = this; this.debounce = requestAnimationFrame(function() {self.update();}); if (this.widget) this.widget.disable(); } }, update: function(first) { if (this.tick == null) return var self = this, myTick = ++this.tick fetchHints(this.options.hint, this.cm, this.options, function(data) { if (self.tick == myTick) self.finishUpdate(data, first) }) }, finishUpdate: function(data, first) { if (this.data) CodeMirror.signal(this.data, "update"); var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); if (this.widget) this.widget.close(); this.data = data; if (data && data.list.length) { if (picked && data.list.length == 1) { this.pick(data, 0); } else { this.widget = new Widget(this, data); CodeMirror.signal(data, "shown"); } } } }; function parseOptions(cm, pos, options) { var editor = cm.options.hintOptions; var out = {}; for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; if (editor) for (var prop in editor) if (editor[prop] !== undefined) out[prop] = editor[prop]; if (options) for (var prop in options) if (options[prop] !== undefined) out[prop] = options[prop]; if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos) return out; } function getText(completion) { if (typeof completion == "string") return completion; else return completion.text; } function buildKeyMap(completion, handle) { var baseMap = { Up: function() {handle.moveFocus(-1);}, Down: function() {handle.moveFocus(1);}, PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, Home: function() {handle.setFocus(0);}, End: function() {handle.setFocus(handle.length - 1);}, Enter: handle.pick, Tab: handle.pick, Esc: handle.close }; var custom = completion.options.customKeys; var ourMap = custom ? {} : baseMap; function addBinding(key, val) { var bound; if (typeof val != "string") bound = function(cm) { return val(cm, handle); }; // This mechanism is deprecated else if (baseMap.hasOwnProperty(val)) bound = baseMap[val]; else bound = val; ourMap[key] = bound; } if (custom) for (var key in custom) if (custom.hasOwnProperty(key)) addBinding(key, custom[key]); var extra = completion.options.extraKeys; if (extra) for (var key in extra) if (extra.hasOwnProperty(key)) addBinding(key, extra[key]); return ourMap; } function getHintElement(hintsElement, el) { while (el && el != hintsElement) { if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; el = el.parentNode; } } function Widget(completion, data) { this.completion = completion; this.data = data; this.picked = false; var widget = this, cm = completion.cm; var hints = this.hints = document.createElement("ul"); var theme = completion.cm.options.theme; hints.className = "CodeMirror-hints " + theme; this.selectedHint = data.selectedHint || 0; var completions = data.list; for (var i = 0; i < completions.length; ++i) { var elt = hints.appendChild(document.createElement("li")), cur = completions[i]; var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); if (cur.className != null) className = cur.className + " " + className; elt.className = className; if (cur.render) cur.render(elt, data, cur); else elt.appendChild(document.createTextNode(cur.displayText || getText(cur))); elt.hintId = i; } var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); var left = pos.left, top = pos.bottom, below = true; hints.style.left = left + "px"; hints.style.top = top + "px"; // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); (completion.options.container || document.body).appendChild(hints); var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; var scrolls = hints.scrollHeight > hints.clientHeight + 1 var startScroll = cm.getScrollInfo(); if (overlapY > 0) { var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); if (curTop - height > 0) { // Fits above cursor hints.style.top = (top = pos.top - height) + "px"; below = false; } else if (height > winH) { hints.style.height = (winH - 5) + "px"; hints.style.top = (top = pos.bottom - box.top) + "px"; var cursor = cm.getCursor(); if (data.from.ch != cursor.ch) { pos = cm.cursorCoords(cursor); hints.style.left = (left = pos.left) + "px"; box = hints.getBoundingClientRect(); } } } var overlapX = box.right - winW; if (overlapX > 0) { if (box.right - box.left > winW) { hints.style.width = (winW - 5) + "px"; overlapX -= (box.right - box.left) - winW; } hints.style.left = (left = pos.left - overlapX) + "px"; } if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) node.style.paddingRight = cm.display.nativeBarWidth + "px" cm.addKeyMap(this.keyMap = buildKeyMap(completion, { moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, setFocus: function(n) { widget.changeActive(n); }, menuSize: function() { return widget.screenAmount(); }, length: completions.length, close: function() { completion.close(); }, pick: function() { widget.pick(); }, data: data })); if (completion.options.closeOnUnfocus) { var closingOnBlur; cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); } cm.on("scroll", this.onScroll = function() { var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); var newTop = top + startScroll.top - curScroll.top; var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop); if (!below) point += hints.offsetHeight; if (point <= editor.top || point >= editor.bottom) return completion.close(); hints.style.top = newTop + "px"; hints.style.left = (left + startScroll.left - curScroll.left) + "px"; }); CodeMirror.on(hints, "dblclick", function(e) { var t = getHintElement(hints, e.target || e.srcElement); if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} }); CodeMirror.on(hints, "click", function(e) { var t = getHintElement(hints, e.target || e.srcElement); if (t && t.hintId != null) { widget.changeActive(t.hintId); if (completion.options.completeOnSingleClick) widget.pick(); } }); CodeMirror.on(hints, "mousedown", function() { setTimeout(function(){cm.focus();}, 20); }); CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); return true; } Widget.prototype = { close: function() { if (this.completion.widget != this) return; this.completion.widget = null; this.hints.parentNode.removeChild(this.hints); this.completion.cm.removeKeyMap(this.keyMap); var cm = this.completion.cm; if (this.completion.options.closeOnUnfocus) { cm.off("blur", this.onBlur); cm.off("focus", this.onFocus); } cm.off("scroll", this.onScroll); }, disable: function() { this.completion.cm.removeKeyMap(this.keyMap); var widget = this; this.keyMap = {Enter: function() { widget.picked = true; }}; this.completion.cm.addKeyMap(this.keyMap); }, pick: function() { this.completion.pick(this.data, this.selectedHint); }, changeActive: function(i, avoidWrap) { if (i >= this.data.list.length) i = avoidWrap ? this.data.list.length - 1 : 0; else if (i < 0) i = avoidWrap ? 0 : this.data.list.length - 1; if (this.selectedHint == i) return; var node = this.hints.childNodes[this.selectedHint]; if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); node = this.hints.childNodes[this.selectedHint = i]; node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; if (node.offsetTop < this.hints.scrollTop) this.hints.scrollTop = node.offsetTop - 3; else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); }, screenAmount: function() { return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; } }; function applicableHelpers(cm, helpers) { if (!cm.somethingSelected()) return helpers var result = [] for (var i = 0; i < helpers.length; i++) if (helpers[i].supportsSelection) result.push(helpers[i]) return result } function fetchHints(hint, cm, options, callback) { if (hint.async) { hint(cm, callback, options) } else { var result = hint(cm, options) if (result && result.then) result.then(callback) else callback(result) } } function resolveAutoHints(cm, pos) { var helpers = cm.getHelpers(pos, "hint"), words if (helpers.length) { var resolved = function(cm, callback, options) { var app = applicableHelpers(cm, helpers); function run(i) { if (i == app.length) return callback(null) fetchHints(app[i], cm, options, function(result) { if (result && result.list.length > 0) callback(result) else run(i + 1) }) } run(0) } resolved.async = true resolved.supportsSelection = true return resolved } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) } } else if (CodeMirror.hint.anyword) { return function(cm, options) { return CodeMirror.hint.anyword(cm, options) } } else { return function() {} } } CodeMirror.registerHelper("hint", "auto", { resolve: resolveAutoHints }); CodeMirror.registerHelper("hint", "fromList", function(cm, options) { var cur = cm.getCursor(), token = cm.getTokenAt(cur) var term, from = CodeMirror.Pos(cur.line, token.start), to = cur if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) { term = token.string.substr(0, cur.ch - token.start) } else { term = "" from = cur } var found = []; for (var i = 0; i < options.words.length; i++) { var word = options.words[i]; if (word.slice(0, term.length) == term) found.push(word); } if (found.length) return {list: found, from: from, to: to}; }); CodeMirror.commands.autocomplete = CodeMirror.showHint; var defaultOptions = { hint: CodeMirror.hint.auto, completeSingle: true, alignWithWord: true, closeCharacters: /[\s()\[\]{};:>,]/, closeOnUnfocus: true, completeOnSingleClick: true, container: null, customKeys: null, extraKeys: null }; CodeMirror.defineOption("hintOptions", null); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/lib/codemirror.css ================================================ /* BASICS */ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; height: 300px; color: black; direction: ltr; } /* PADDING */ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } .CodeMirror pre { padding: 0 4px; /* Horizontal padding of content */ } .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; /* The little square between H and V scrollbars */ } /* GUTTER */ .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: #f7f7f7; white-space: nowrap; } .CodeMirror-linenumbers {} .CodeMirror-linenumber { padding: 0 3px 0 5px; min-width: 20px; text-align: right; color: #999; white-space: nowrap; } .CodeMirror-guttermarker { color: black; } .CodeMirror-guttermarker-subtle { color: #999; } /* CURSOR */ .CodeMirror-cursor { border-left: 1px solid black; border-right: none; width: 0; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { border-left: 1px solid silver; } .cm-fat-cursor .CodeMirror-cursor { width: auto; border: 0 !important; background: #7e7; } .cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } .cm-fat-cursor-mark { background-color: rgba(20, 255, 20, 0.5); -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; } .cm-animate-fat-cursor { width: auto; border: 0; -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; background-color: #7e7; } @-moz-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @-webkit-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } /* Can style cursor different in overwrite (non-insert) mode */ .CodeMirror-overwrite .CodeMirror-cursor {} .cm-tab { display: inline-block; text-decoration: inherit; } .CodeMirror-rulers { position: absolute; left: 0; right: 0; top: -50px; bottom: -20px; overflow: hidden; } .CodeMirror-ruler { border-left: 1px solid #ccc; top: 0; bottom: 0; position: absolute; } /* DEFAULT THEME */ .cm-s-default .cm-header {color: blue;} .cm-s-default .cm-quote {color: #090;} .cm-negative {color: #d44;} .cm-positive {color: #292;} .cm-header, .cm-strong {font-weight: bold;} .cm-em {font-style: italic;} .cm-link {text-decoration: underline;} .cm-strikethrough {text-decoration: line-through;} .cm-s-default .cm-keyword {color: #708;} .cm-s-default .cm-atom {color: #219;} .cm-s-default .cm-number {color: #164;} .cm-s-default .cm-def {color: #00f;} .cm-s-default .cm-variable, .cm-s-default .cm-punctuation, .cm-s-default .cm-property, .cm-s-default .cm-operator {} .cm-s-default .cm-variable-2 {color: #05a;} .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} .cm-s-default .cm-comment {color: #a50;} .cm-s-default .cm-string {color: #a11;} .cm-s-default .cm-string-2 {color: #f50;} .cm-s-default .cm-meta {color: #555;} .cm-s-default .cm-qualifier {color: #555;} .cm-s-default .cm-builtin {color: #30a;} .cm-s-default .cm-bracket {color: #997;} .cm-s-default .cm-tag {color: #170;} .cm-s-default .cm-attribute {color: #00c;} .cm-s-default .cm-hr {color: #999;} .cm-s-default .cm-link {color: #00c;} .cm-s-default .cm-error {color: #f00;} .cm-invalidchar {color: #f00;} .CodeMirror-composing { border-bottom: 2px solid; } /* Default styles for common addons */ div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } .CodeMirror-activeline-background {background: #e8f2ff;} /* STOP */ /* The rest of this file contains styles related to the mechanics of the editor. You probably shouldn't touch them. */ .CodeMirror { position: relative; overflow: hidden; background: white; } .CodeMirror-scroll { overflow: scroll !important; /* Things will break if this is overridden */ /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ margin-bottom: -30px; margin-right: -30px; padding-bottom: 30px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; } .CodeMirror-sizer { position: relative; border-right: 30px solid transparent; } /* The fake, visible scrollbars. Used to force redraw during scrolling before actual scrolling happens, thus preventing shaking and flickering artifacts. */ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; z-index: 6; display: none; } .CodeMirror-vscrollbar { right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; } .CodeMirror-hscrollbar { bottom: 0; left: 0; overflow-y: hidden; overflow-x: scroll; } .CodeMirror-scrollbar-filler { right: 0; bottom: 0; } .CodeMirror-gutter-filler { left: 0; bottom: 0; } .CodeMirror-gutters { position: absolute; left: 0; top: 0; min-height: 100%; z-index: 3; } .CodeMirror-gutter { white-space: normal; height: 100%; display: inline-block; vertical-align: top; margin-bottom: -30px; } .CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: none !important; border: none !important; } .CodeMirror-gutter-background { position: absolute; top: 0; bottom: 0; z-index: 4; } .CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; } .CodeMirror-gutter-wrapper ::selection { background-color: transparent } .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } .CodeMirror-lines { cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; font-size: inherit; margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; z-index: 2; position: relative; overflow: visible; -webkit-tap-highlight-color: transparent; -webkit-font-variant-ligatures: contextual; font-variant-ligatures: contextual; } .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; } .CodeMirror-linewidget { position: relative; z-index: 2; padding: 0.1px; /* Force widget margins to stay inside of the container */ } .CodeMirror-widget {} .CodeMirror-rtl pre { direction: rtl; } .CodeMirror-code { outline: none; } /* Force content-box sizing for the elements where we expect it */ .CodeMirror-scroll, .CodeMirror-sizer, .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber { -moz-box-sizing: content-box; box-sizing: content-box; } .CodeMirror-measure { position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden; } .CodeMirror-cursor { position: absolute; pointer-events: none; } .CodeMirror-measure pre { position: static; } div.CodeMirror-cursors { visibility: hidden; position: relative; z-index: 3; } div.CodeMirror-dragcursors { visibility: visible; } .CodeMirror-focused div.CodeMirror-cursors { visibility: visible; } .CodeMirror-selected { background: #d9d9d9; } .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-crosshair { cursor: crosshair; } .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } .cm-searching { background-color: #ffa; background-color: rgba(255, 255, 0, .4); } /* Used to force a border model for a node */ .cm-force-border { padding-right: .1px; } @media print { /* Hide the cursor when printing */ .CodeMirror div.CodeMirror-cursors { visibility: hidden; } } /* See issue #2901 */ .cm-tab-wrap-hack:after { content: ''; } /* Help users use markselection to safely style text background */ span.CodeMirror-selectedtext { background: none; } ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/lib/codemirror.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE // This is CodeMirror (https://codemirror.net), a code editor // implemented in JavaScript on top of the browser's DOM. // // You can find some technical background for some of the code below // at http://marijnhaverbeke.nl/blog/#cm-internals . (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.CodeMirror = factory()); }(this, (function () { 'use strict'; // Kludges for bugs and behavior differences that can't be feature // detected are enabled based on userAgent etc sniffing. var userAgent = navigator.userAgent; var platform = navigator.platform; var gecko = /gecko\/\d/i.test(userAgent); var ie_upto10 = /MSIE \d/.test(userAgent); var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); var edge = /Edge\/(\d+)/.exec(userAgent); var ie = ie_upto10 || ie_11up || edge; var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); var webkit = !edge && /WebKit\//.test(userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); var chrome = !edge && /Chrome\//.test(userAgent); var presto = /Opera\//.test(userAgent); var safari = /Apple Computer/.test(navigator.vendor); var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); var phantom = /PhantomJS/.test(userAgent); var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); var android = /Android/.test(userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); var mac = ios || /Mac/.test(platform); var chromeOS = /\bCrOS\b/.test(userAgent); var windows = /win/i.test(platform); var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); if (presto_version) { presto_version = Number(presto_version[1]); } if (presto_version && presto_version >= 15) { presto = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); var captureRightClick = gecko || (ie && ie_version >= 9); function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } var rmClass = function(node, cls) { var current = node.className; var match = classTest(cls).exec(current); if (match) { var after = current.slice(match.index + match[0].length); node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); } }; function removeChildren(e) { for (var count = e.childNodes.length; count > 0; --count) { e.removeChild(e.firstChild); } return e } function removeChildrenAndAdd(parent, e) { return removeChildren(parent).appendChild(e) } function elt(tag, content, className, style) { var e = document.createElement(tag); if (className) { e.className = className; } if (style) { e.style.cssText = style; } if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } return e } // wrapper for elt, which removes the elt from the accessibility tree function eltP(tag, content, className, style) { var e = elt(tag, content, className, style); e.setAttribute("role", "presentation"); return e } var range; if (document.createRange) { range = function(node, start, end, endNode) { var r = document.createRange(); r.setEnd(endNode || node, end); r.setStart(node, start); return r }; } else { range = function(node, start, end) { var r = document.body.createTextRange(); try { r.moveToElementText(node.parentNode); } catch(e) { return r } r.collapse(true); r.moveEnd("character", end); r.moveStart("character", start); return r }; } function contains(parent, child) { if (child.nodeType == 3) // Android browser always returns false when child is a textnode { child = child.parentNode; } if (parent.contains) { return parent.contains(child) } do { if (child.nodeType == 11) { child = child.host; } if (child == parent) { return true } } while (child = child.parentNode) } function activeElt() { // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. // IE < 10 will throw when accessed while the page is loading or in an iframe. // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. var activeElement; try { activeElement = document.activeElement; } catch(e) { activeElement = document.body || null; } while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) { activeElement = activeElement.shadowRoot.activeElement; } return activeElement } function addClass(node, cls) { var current = node.className; if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } } function joinClasses(a, b) { var as = a.split(" "); for (var i = 0; i < as.length; i++) { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } return b } var selectInput = function(node) { node.select(); }; if (ios) // Mobile Safari apparently has a bug where select() is broken. { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } else if (ie) // Suppress mysterious IE10 errors { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } function bind(f) { var args = Array.prototype.slice.call(arguments, 1); return function(){return f.apply(null, args)} } function copyObj(obj, target, overwrite) { if (!target) { target = {}; } for (var prop in obj) { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) { target[prop] = obj[prop]; } } return target } // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. function countColumn(string, end, tabSize, startIndex, startValue) { if (end == null) { end = string.search(/[^\s\u00a0]/); if (end == -1) { end = string.length; } } for (var i = startIndex || 0, n = startValue || 0;;) { var nextTab = string.indexOf("\t", i); if (nextTab < 0 || nextTab >= end) { return n + (end - i) } n += nextTab - i; n += tabSize - (n % tabSize); i = nextTab + 1; } } var Delayed = function() {this.id = null;}; Delayed.prototype.set = function (ms, f) { clearTimeout(this.id); this.id = setTimeout(f, ms); }; function indexOf(array, elt) { for (var i = 0; i < array.length; ++i) { if (array[i] == elt) { return i } } return -1 } // Number of pixels added to scroller and sizer to hide scrollbar var scrollerGap = 30; // Returned or thrown by various protocols to signal 'I'm not // handling this'. var Pass = {toString: function(){return "CodeMirror.Pass"}}; // Reused option objects for setSelection & friends var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; // The inverse of countColumn -- find the offset that corresponds to // a particular column. function findColumn(string, goal, tabSize) { for (var pos = 0, col = 0;;) { var nextTab = string.indexOf("\t", pos); if (nextTab == -1) { nextTab = string.length; } var skipped = nextTab - pos; if (nextTab == string.length || col + skipped >= goal) { return pos + Math.min(skipped, goal - col) } col += nextTab - pos; col += tabSize - (col % tabSize); pos = nextTab + 1; if (col >= goal) { return pos } } } var spaceStrs = [""]; function spaceStr(n) { while (spaceStrs.length <= n) { spaceStrs.push(lst(spaceStrs) + " "); } return spaceStrs[n] } function lst(arr) { return arr[arr.length-1] } function map(array, f) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } return out } function insertSorted(array, value, score) { var pos = 0, priority = score(value); while (pos < array.length && score(array[pos]) <= priority) { pos++; } array.splice(pos, 0, value); } function nothing() {} function createObj(base, props) { var inst; if (Object.create) { inst = Object.create(base); } else { nothing.prototype = base; inst = new nothing(); } if (props) { copyObj(props, inst); } return inst } var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; function isWordCharBasic(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) } function isWordChar(ch, helper) { if (!helper) { return isWordCharBasic(ch) } if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } return helper.test(ch) } function isEmpty(obj) { for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } return true } // Extending unicode characters. A series of a non-extending char + // any number of extending chars is treated as a single unit as far // as editing and measuring is concerned. This is not fully correct, // since some scripts/fonts/browsers also treat other configurations // of code points as a group. var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. function skipExtendingChars(str, pos, dir) { while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } return pos } // Returns the value from the range [`from`; `to`] that satisfies // `pred` and is closest to `from`. Assumes that at least `to` // satisfies `pred`. Supports `from` being greater than `to`. function findFirst(pred, from, to) { // At any point we are certain `to` satisfies `pred`, don't know // whether `from` does. var dir = from > to ? -1 : 1; for (;;) { if (from == to) { return from } var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); if (mid == from) { return pred(mid) ? from : to } if (pred(mid)) { to = mid; } else { from = mid + dir; } } } // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and // display-related state. function Display(place, doc, input) { var d = this; this.input = input; // Covers bottom-right square when both scrollbars are present. d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); d.scrollbarFiller.setAttribute("cm-not-content", "true"); // Covers bottom of gutter when coverGutterNextToScrollbar is on // and h scrollbar is present. d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); d.gutterFiller.setAttribute("cm-not-content", "true"); // Will contain the actual code, positioned to cover the viewport. d.lineDiv = eltP("div", null, "CodeMirror-code"); // Elements are added to these to represent selection and cursors. d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); d.cursorDiv = elt("div", null, "CodeMirror-cursors"); // A visibility: hidden element used to find the size of things. d.measure = elt("div", null, "CodeMirror-measure"); // When lines outside of the viewport are measured, they are drawn in this. d.lineMeasure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], null, "position: relative; outline: none"); var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); // Moved around its parent to cover visible view. d.mover = elt("div", [lines], null, "position: relative"); // Set to the height of the document, allowing scrolling. d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); d.sizerWidth = null; // Behavior of elts with overflow: auto and padding is // inconsistent across browsers. This is used to ensure the // scrollable area is big enough. d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); // Will contain the gutters, if any. d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; // Actual scrollable element. d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } if (place) { if (place.appendChild) { place.appendChild(d.wrapper); } else { place(d.wrapper); } } // Current rendered range (may be bigger than the view window). d.viewFrom = d.viewTo = doc.first; d.reportedViewFrom = d.reportedViewTo = doc.first; // Information about the rendered lines. d.view = []; d.renderedView = null; // Holds info about a single rendered line when it was rendered // for measurement, while not in view. d.externalMeasured = null; // Empty space (in pixels) above the view d.viewOffset = 0; d.lastWrapHeight = d.lastWrapWidth = 0; d.updateLineNumbers = null; d.nativeBarWidth = d.barHeight = d.barWidth = 0; d.scrollbarsClipped = false; // Used to only resize the line number gutter when necessary (when // the amount of lines crosses a boundary that makes its width change) d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; // Set to true when a non-horizontal-scrolling line widget is // added. As an optimization, line widget aligning is skipped when // this is false. d.alignWidgets = false; d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. d.maxLine = null; d.maxLineLength = 0; d.maxLineChanged = false; // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; // True when shift is held down. d.shift = false; // Used to track whether anything happened since the context menu // was opened. d.selForContextMenu = null; d.activeTouch = null; input.init(d); } // Find the line object corresponding to the given line number. function getLine(doc, n) { n -= doc.first; if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } var chunk = doc; while (!chunk.lines) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); if (n < sz) { chunk = child; break } n -= sz; } } return chunk.lines[n] } // Get the part of a document between two positions, as an array of // strings. function getBetween(doc, start, end) { var out = [], n = start.line; doc.iter(start.line, end.line + 1, function (line) { var text = line.text; if (n == end.line) { text = text.slice(0, end.ch); } if (n == start.line) { text = text.slice(start.ch); } out.push(text); ++n; }); return out } // Get the lines between from and to, as array of strings. function getLines(doc, from, to) { var out = []; doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value return out } // Update the height of a line, propagating the height change // upwards to parent nodes. function updateLineHeight(line, height) { var diff = height - line.height; if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } } // Given a line object, find its line number by walking up through // its parent links. function lineNo(line) { if (line.parent == null) { return null } var cur = line.parent, no = indexOf(cur.lines, line); for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { for (var i = 0;; ++i) { if (chunk.children[i] == cur) { break } no += chunk.children[i].chunkSize(); } } return no + cur.first } // Find the line at the given vertical position, using the height // information in the document tree. function lineAtHeight(chunk, h) { var n = chunk.first; outer: do { for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { var child = chunk.children[i$1], ch = child.height; if (h < ch) { chunk = child; continue outer } h -= ch; n += child.chunkSize(); } return n } while (!chunk.lines) var i = 0; for (; i < chunk.lines.length; ++i) { var line = chunk.lines[i], lh = line.height; if (h < lh) { break } h -= lh; } return n + i } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} function lineNumberFor(options, i) { return String(options.lineNumberFormatter(i + options.firstLineNumber)) } // A Pos instance represents a position within the text. function Pos(line, ch, sticky) { if ( sticky === void 0 ) sticky = null; if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } this.line = line; this.ch = ch; this.sticky = sticky; } // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. function cmp(a, b) { return a.line - b.line || a.ch - b.ch } function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } function copyPos(x) {return Pos(x.line, x.ch)} function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } function minPos(a, b) { return cmp(a, b) < 0 ? a : b } // Most of the external API clips given positions to make sure they // actually exist within the document. function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} function clipPos(doc, pos) { if (pos.line < doc.first) { return Pos(doc.first, 0) } var last = doc.first + doc.size - 1; if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } return clipToLen(pos, getLine(doc, pos.line).text.length) } function clipToLen(pos, linelen) { var ch = pos.ch; if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } else if (ch < 0) { return Pos(pos.line, 0) } else { return pos } } function clipPosArray(doc, array) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } return out } // Optimize some code when these features are not used. var sawReadOnlySpans = false, sawCollapsedSpans = false; function seeReadOnlySpans() { sawReadOnlySpans = true; } function seeCollapsedSpans() { sawCollapsedSpans = true; } // TEXTMARKER SPANS function MarkedSpan(marker, from, to) { this.marker = marker; this.from = from; this.to = to; } // Search an array of spans for a span matching the given marker. function getMarkedSpanFor(spans, marker) { if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.marker == marker) { return span } } } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). function removeMarkedSpan(spans, span) { var r; for (var i = 0; i < spans.length; ++i) { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } return r } // Add a span to a line. function addMarkedSpan(line, span) { line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; span.marker.attachLine(line); } // Used for the algorithm that adjusts markers for a change in the // document. These functions cut an array of spans at a given // character position, returning an array of remaining chunks (or // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); } } } return nw } function markedSpansAfter(old, endCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, span.to == null ? null : span.to - endCh)); } } } return nw } // Given a change object, compute the new set of marker spans that // cover the line in which the change took place. Removes spans // entirely within the change, reconnects spans belonging to the // same marker that appear on both sides of the change, and cuts off // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. function stretchSpansOverChange(doc, change) { if (change.full) { return null } var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; if (!oldFirst && !oldLast) { return null } var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; // Get the spans that 'stick out' on both sides var first = markedSpansBefore(oldFirst, startCh, isInsert); var last = markedSpansAfter(oldLast, endCh, isInsert); // Next, merge those two ends var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); if (first) { // Fix up .to properties of first for (var i = 0; i < first.length; ++i) { var span = first[i]; if (span.to == null) { var found = getMarkedSpanFor(last, span.marker); if (!found) { span.to = startCh; } else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } } } } if (last) { // Fix up .from in last (or move them into first in case of sameLine) for (var i$1 = 0; i$1 < last.length; ++i$1) { var span$1 = last[i$1]; if (span$1.to != null) { span$1.to += offset; } if (span$1.from == null) { var found$1 = getMarkedSpanFor(first, span$1.marker); if (!found$1) { span$1.from = offset; if (sameLine) { (first || (first = [])).push(span$1); } } } else { span$1.from += offset; if (sameLine) { (first || (first = [])).push(span$1); } } } } // Make sure we didn't create any zero-length spans if (first) { first = clearEmptySpans(first); } if (last && last != first) { last = clearEmptySpans(last); } var newMarkers = [first]; if (!sameLine) { // Fill gap with whole-line-spans var gap = change.text.length - 2, gapMarkers; if (gap > 0 && first) { for (var i$2 = 0; i$2 < first.length; ++i$2) { if (first[i$2].to == null) { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } for (var i$3 = 0; i$3 < gap; ++i$3) { newMarkers.push(gapMarkers); } newMarkers.push(last); } return newMarkers } // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) { spans.splice(i--, 1); } } if (!spans.length) { return null } return spans } // Used to 'clip' out readOnly ranges when making a change. function removeReadOnlyRanges(doc, from, to) { var markers = null; doc.iter(from.line, to.line + 1, function (line) { if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var mark = line.markedSpans[i].marker; if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) { (markers || (markers = [])).push(mark); } } } }); if (!markers) { return null } var parts = [{from: from, to: to}]; for (var i = 0; i < markers.length; ++i) { var mk = markers[i], m = mk.find(0); for (var j = 0; j < parts.length; ++j) { var p = parts[j]; if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) { newParts.push({from: p.from, to: m.from}); } if (dto > 0 || !mk.inclusiveRight && !dto) { newParts.push({from: m.to, to: p.to}); } parts.splice.apply(parts, newParts); j += newParts.length - 3; } } return parts } // Connect or disconnect spans from a line. function detachMarkedSpans(line) { var spans = line.markedSpans; if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.detachLine(line); } line.markedSpans = null; } function attachMarkedSpans(line, spans) { if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.attachLine(line); } line.markedSpans = spans; } // Helpers used when computing which overlapping collapsed span // counts as the larger one. function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } // Returns a number indicating which of two overlapping collapsed // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. function compareCollapsedMarkers(a, b) { var lenDiff = a.lines.length - b.lines.length; if (lenDiff != 0) { return lenDiff } var aPos = a.find(), bPos = b.find(); var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); if (fromCmp) { return -fromCmp } var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); if (toCmp) { return toCmp } return b.id - a.id } // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } } } return found } function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } function collapsedSpanAround(line, ch) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } } } return found } // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) { var line = getLine(doc, lineNo$$1); var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (!sp.marker.collapsed) { continue } var found = sp.marker.find(0); var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) { return true } } } } // A visual line is a line as drawn on the screen. Folding, for // example, can cause multiple logical lines to appear on the same // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). function visualLine(line) { var merged; while (merged = collapsedSpanAtStart(line)) { line = merged.find(-1, true).line; } return line } function visualLineEnd(line) { var merged; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return line } // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. function visualLineContinued(line) { var merged, lines; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line ;(lines || (lines = [])).push(line); } return lines } // Get the line number of the start of the visual line that the // given line number is part of. function visualLineNo(doc, lineN) { var line = getLine(doc, lineN), vis = visualLine(line); if (line == vis) { return lineN } return lineNo(vis) } // Get the line number of the start of the next visual line after // the given line. function visualLineEndNo(doc, lineN) { if (lineN > doc.lastLine()) { return lineN } var line = getLine(doc, lineN), merged; if (!lineIsHidden(doc, line)) { return lineN } while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return lineNo(line) + 1 } // Compute whether a line is hidden. Lines count as hidden when they // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. function lineIsHidden(doc, line) { var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (!sp.marker.collapsed) { continue } if (sp.from == null) { return true } if (sp.marker.widgetNode) { continue } if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) { return true } } } } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { var end = span.marker.find(1, true); return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) } if (span.marker.inclusiveRight && span.to == line.text.length) { return true } for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) { return true } } } // Find the height above the given line. function heightAtLine(lineObj) { lineObj = visualLine(lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { var line = chunk.lines[i]; if (line == lineObj) { break } else { h += line.height; } } for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { for (var i$1 = 0; i$1 < p.children.length; ++i$1) { var cur = p.children[i$1]; if (cur == chunk) { break } else { h += cur.height; } } } return h } // Compute the character length of a line, taking into account // collapsed ranges (see markText) that might hide parts, and join // other lines onto it. function lineLength(line) { if (line.height == 0) { return 0 } var len = line.text.length, merged, cur = line; while (merged = collapsedSpanAtStart(cur)) { var found = merged.find(0, true); cur = found.from.line; len += found.from.ch - found.to.ch; } cur = line; while (merged = collapsedSpanAtEnd(cur)) { var found$1 = merged.find(0, true); len -= cur.text.length - found$1.from.ch; cur = found$1.to.line; len += cur.text.length - found$1.to.ch; } return len } // Find the longest line in the document. function findMaxLine(cm) { var d = cm.display, doc = cm.doc; d.maxLine = getLine(doc, doc.first); d.maxLineLength = lineLength(d.maxLine); d.maxLineChanged = true; doc.iter(function (line) { var len = lineLength(line); if (len > d.maxLineLength) { d.maxLineLength = len; d.maxLine = line; } }); } // BIDI HELPERS function iterateBidiSections(order, from, to, f) { if (!order) { return f(from, to, "ltr", 0) } var found = false; for (var i = 0; i < order.length; ++i) { var part = order[i]; if (part.from < to && part.to > from || from == to && part.to == from) { f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); found = true; } } if (!found) { f(from, to, "ltr"); } } var bidiOther = null; function getBidiPartAt(order, ch, sticky) { var found; bidiOther = null; for (var i = 0; i < order.length; ++i) { var cur = order[i]; if (cur.from < ch && cur.to > ch) { return i } if (cur.to == ch) { if (cur.from != cur.to && sticky == "before") { found = i; } else { bidiOther = i; } } if (cur.from == ch) { if (cur.from != cur.to && sticky != "before") { found = i; } else { bidiOther = i; } } } return found != null ? found : bidiOther } // Bidirectional ordering algorithm // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm // that this (partially) implements. // One-char codes used for character types: // L (L): Left-to-Right // R (R): Right-to-Left // r (AL): Right-to-Left Arabic // 1 (EN): European Number // + (ES): European Number Separator // % (ET): European Number Terminator // n (AN): Arabic Number // , (CS): Common Number Separator // m (NSM): Non-Spacing Mark // b (BN): Boundary Neutral // s (B): Paragraph Separator // t (S): Segment Separator // w (WS): Whitespace // N (ON): Other Neutrals // Returns null if characters are ordered as they appear // (left-to-right), or an array of sections ({from, to, level} // objects) in the order in which they occur visually. var bidiOrdering = (function() { // Character types for codepoints 0 to 0xff var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; // Character types for codepoints 0x600 to 0x6f9 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; function charType(code) { if (code <= 0xf7) { return lowTypes.charAt(code) } else if (0x590 <= code && code <= 0x5f4) { return "R" } else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } else if (0x6ee <= code && code <= 0x8ac) { return "r" } else if (0x2000 <= code && code <= 0x200b) { return "w" } else if (code == 0x200c) { return "b" } else { return "L" } } var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; function BidiSpan(level, from, to) { this.level = level; this.from = from; this.to = to; } return function(str, direction) { var outerType = direction == "ltr" ? "L" : "R"; if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } var len = str.length, types = []; for (var i = 0; i < len; ++i) { types.push(charType(str.charCodeAt(i))); } // W1. Examine each non-spacing mark (NSM) in the level run, and // change the type of the NSM to the type of the previous // character. If the NSM is at the start of the level run, it will // get the type of sor. for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { var type = types[i$1]; if (type == "m") { types[i$1] = prev; } else { prev = type; } } // W2. Search backwards from each instance of a European number // until the first strong type (R, L, AL, or sor) is found. If an // AL is found, change the type of the European number to Arabic // number. // W3. Change all ALs to R. for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { var type$1 = types[i$2]; if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } } // W4. A single European separator between two European numbers // changes to a European number. A single common separator between // two numbers of the same type changes to that type. for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { var type$2 = types[i$3]; if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } else if (type$2 == "," && prev$1 == types[i$3+1] && (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } prev$1 = type$2; } // W5. A sequence of European terminators adjacent to European // numbers changes to all European numbers. // W6. Otherwise, separators and terminators change to Other // Neutral. for (var i$4 = 0; i$4 < len; ++i$4) { var type$3 = types[i$4]; if (type$3 == ",") { types[i$4] = "N"; } else if (type$3 == "%") { var end = (void 0); for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; for (var j = i$4; j < end; ++j) { types[j] = replace; } i$4 = end - 1; } } // W7. Search backwards from each instance of a European number // until the first strong type (R, L, or sor) is found. If an L is // found, then change the type of the European number to L. for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { var type$4 = types[i$5]; if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } else if (isStrong.test(type$4)) { cur$1 = type$4; } } // N1. A sequence of neutrals takes the direction of the // surrounding strong text if the text on both sides has the same // direction. European and Arabic numbers act as if they were R in // terms of their influence on neutrals. Start-of-level-run (sor) // and end-of-level-run (eor) are used at level run boundaries. // N2. Any remaining neutrals take the embedding direction. for (var i$6 = 0; i$6 < len; ++i$6) { if (isNeutral.test(types[i$6])) { var end$1 = (void 0); for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} var before = (i$6 ? types[i$6-1] : outerType) == "L"; var after = (end$1 < len ? types[end$1] : outerType) == "L"; var replace$1 = before == after ? (before ? "L" : "R") : outerType; for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } i$6 = end$1 - 1; } } // Here we depart from the documented algorithm, in order to avoid // building up an actual levels array. Since there are only three // levels (0, 1, 2) in an implementation that doesn't take // explicit embedding into account, we can build up the order on // the fly, without following the level-based algorithm. var order = [], m; for (var i$7 = 0; i$7 < len;) { if (countsAsLeft.test(types[i$7])) { var start = i$7; for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} order.push(new BidiSpan(0, start, i$7)); } else { var pos = i$7, at = order.length; for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} for (var j$2 = pos; j$2 < i$7;) { if (countsAsNum.test(types[j$2])) { if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } var nstart = j$2; for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} order.splice(at, 0, new BidiSpan(2, nstart, j$2)); pos = j$2; } else { ++j$2; } } if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } } } if (direction == "ltr") { if (order[0].level == 1 && (m = str.match(/^\s+/))) { order[0].from = m[0].length; order.unshift(new BidiSpan(0, 0, m[0].length)); } if (lst(order).level == 1 && (m = str.match(/\s+$/))) { lst(order).to -= m[0].length; order.push(new BidiSpan(0, len - m[0].length, len)); } } return direction == "rtl" ? order.reverse() : order } })(); // Get the bidi ordering for the given line (and cache it). Returns // false for lines that are fully left-to-right, and an array of // BidiSpan objects otherwise. function getOrder(line, direction) { var order = line.order; if (order == null) { order = line.order = bidiOrdering(line.text, direction); } return order } // EVENT HANDLING // Lightweight event framework. on/off also work on DOM nodes, // registering native DOM handlers. var noHandlers = []; var on = function(emitter, type, f) { if (emitter.addEventListener) { emitter.addEventListener(type, f, false); } else if (emitter.attachEvent) { emitter.attachEvent("on" + type, f); } else { var map$$1 = emitter._handlers || (emitter._handlers = {}); map$$1[type] = (map$$1[type] || noHandlers).concat(f); } }; function getHandlers(emitter, type) { return emitter._handlers && emitter._handlers[type] || noHandlers } function off(emitter, type, f) { if (emitter.removeEventListener) { emitter.removeEventListener(type, f, false); } else if (emitter.detachEvent) { emitter.detachEvent("on" + type, f); } else { var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]; if (arr) { var index = indexOf(arr, f); if (index > -1) { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } } } } function signal(emitter, type /*, values...*/) { var handlers = getHandlers(emitter, type); if (!handlers.length) { return } var args = Array.prototype.slice.call(arguments, 2); for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } } // The DOM events that CodeMirror handles can be overridden by // registering a (non-DOM) handler on the editor for the event name, // and preventDefault-ing the event in that handler. function signalDOMEvent(cm, e, override) { if (typeof e == "string") { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } signal(cm, override || e.type, cm, e); return e_defaultPrevented(e) || e.codemirrorIgnore } function signalCursorActivity(cm) { var arr = cm._handlers && cm._handlers.cursorActivity; if (!arr) { return } var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) { set.push(arr[i]); } } } function hasHandler(emitter, type) { return getHandlers(emitter, type).length > 0 } // Add on and off methods to a constructor's prototype, to make // registering events on such objects more convenient. function eventMixin(ctor) { ctor.prototype.on = function(type, f) {on(this, type, f);}; ctor.prototype.off = function(type, f) {off(this, type, f);}; } // Due to the fact that we still support jurassic IE versions, some // compatibility wrappers are needed. function e_preventDefault(e) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } } function e_stopPropagation(e) { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } function e_defaultPrevented(e) { return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false } function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} function e_target(e) {return e.target || e.srcElement} function e_button(e) { var b = e.which; if (b == null) { if (e.button & 1) { b = 1; } else if (e.button & 2) { b = 3; } else if (e.button & 4) { b = 2; } } if (mac && e.ctrlKey && b == 1) { b = 3; } return b } // Detect drag-and-drop var dragAndDrop = function() { // There is *some* kind of drag-and-drop support in IE6-8, but I // couldn't get it to work yet. if (ie && ie_version < 9) { return false } var div = elt('div'); return "draggable" in div || "dragDrop" in div }(); var zwspSupported; function zeroWidthElement(measure) { if (zwspSupported == null) { var test = elt("span", "\u200b"); removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); if (measure.firstChild.offsetHeight != 0) { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } } var node = zwspSupported ? elt("span", "\u200b") : elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); node.setAttribute("cm-text", ""); return node } // Feature-detect IE's crummy client rect reporting for bidi text var badBidiRects; function hasBadBidiRects(measure) { if (badBidiRects != null) { return badBidiRects } var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); var r0 = range(txt, 0, 1).getBoundingClientRect(); var r1 = range(txt, 1, 2).getBoundingClientRect(); removeChildren(measure); if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) return badBidiRects = (r1.right - r0.right < 3) } // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { var pos = 0, result = [], l = string.length; while (pos <= l) { var nl = string.indexOf("\n", pos); if (nl == -1) { nl = string.length; } var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); var rt = line.indexOf("\r"); if (rt != -1) { result.push(line.slice(0, rt)); pos += rt + 1; } else { result.push(line); pos = nl + 1; } } return result } : function (string) { return string.split(/\r\n?|\n/); }; var hasSelection = window.getSelection ? function (te) { try { return te.selectionStart != te.selectionEnd } catch(e) { return false } } : function (te) { var range$$1; try {range$$1 = te.ownerDocument.selection.createRange();} catch(e) {} if (!range$$1 || range$$1.parentElement() != te) { return false } return range$$1.compareEndPoints("StartToEnd", range$$1) != 0 }; var hasCopyEvent = (function () { var e = elt("div"); if ("oncopy" in e) { return true } e.setAttribute("oncopy", "return;"); return typeof e.oncopy == "function" })(); var badZoomedRects = null; function hasBadZoomedRects(measure) { if (badZoomedRects != null) { return badZoomedRects } var node = removeChildrenAndAdd(measure, elt("span", "x")); var normal = node.getBoundingClientRect(); var fromRange = range(node, 0, 1).getBoundingClientRect(); return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 } // Known modes, by name and by MIME var modes = {}, mimeModes = {}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) function defineMode(name, mode) { if (arguments.length > 2) { mode.dependencies = Array.prototype.slice.call(arguments, 2); } modes[name] = mode; } function defineMIME(mime, spec) { mimeModes[mime] = spec; } // Given a MIME type, a {name, ...options} config object, or a name // string, return a mode config object. function resolveMode(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { var found = mimeModes[spec.name]; if (typeof found == "string") { found = {name: found}; } spec = createObj(found, spec); spec.name = found.name; } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return resolveMode("application/xml") } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { return resolveMode("application/json") } if (typeof spec == "string") { return {name: spec} } else { return spec || {name: "null"} } } // Given a mode spec (anything that resolveMode accepts), find and // initialize an actual mode object. function getMode(options, spec) { spec = resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) { return getMode(options, "text/plain") } var modeObj = mfactory(options, spec); if (modeExtensions.hasOwnProperty(spec.name)) { var exts = modeExtensions[spec.name]; for (var prop in exts) { if (!exts.hasOwnProperty(prop)) { continue } if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } modeObj[prop] = exts[prop]; } } modeObj.name = spec.name; if (spec.helperType) { modeObj.helperType = spec.helperType; } if (spec.modeProps) { for (var prop$1 in spec.modeProps) { modeObj[prop$1] = spec.modeProps[prop$1]; } } return modeObj } // This can be used to attach properties to mode objects from // outside the actual mode definition. var modeExtensions = {}; function extendMode(mode, properties) { var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); copyObj(properties, exts); } function copyState(mode, state) { if (state === true) { return state } if (mode.copyState) { return mode.copyState(state) } var nstate = {}; for (var n in state) { var val = state[n]; if (val instanceof Array) { val = val.concat([]); } nstate[n] = val; } return nstate } // Given a mode and a state (for that mode), find the inner mode and // state at the position that the state refers to. function innerMode(mode, state) { var info; while (mode.innerMode) { info = mode.innerMode(state); if (!info || info.mode == mode) { break } state = info.state; mode = info.mode; } return info || {mode: mode, state: state} } function startState(mode, a1, a2) { return mode.startState ? mode.startState(a1, a2) : true } // STRING STREAM // Fed to the mode parsers, provides helper functions to make // parsers more succinct. var StringStream = function(string, tabSize, lineOracle) { this.pos = this.start = 0; this.string = string; this.tabSize = tabSize || 8; this.lastColumnPos = this.lastColumnValue = 0; this.lineStart = 0; this.lineOracle = lineOracle; }; StringStream.prototype.eol = function () {return this.pos >= this.string.length}; StringStream.prototype.sol = function () {return this.pos == this.lineStart}; StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; StringStream.prototype.next = function () { if (this.pos < this.string.length) { return this.string.charAt(this.pos++) } }; StringStream.prototype.eat = function (match) { var ch = this.string.charAt(this.pos); var ok; if (typeof match == "string") { ok = ch == match; } else { ok = ch && (match.test ? match.test(ch) : match(ch)); } if (ok) {++this.pos; return ch} }; StringStream.prototype.eatWhile = function (match) { var start = this.pos; while (this.eat(match)){} return this.pos > start }; StringStream.prototype.eatSpace = function () { var this$1 = this; var start = this.pos; while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; } return this.pos > start }; StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; StringStream.prototype.skipTo = function (ch) { var found = this.string.indexOf(ch, this.pos); if (found > -1) {this.pos = found; return true} }; StringStream.prototype.backUp = function (n) {this.pos -= n;}; StringStream.prototype.column = function () { if (this.lastColumnPos < this.start) { this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); this.lastColumnPos = this.start; } return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.indentation = function () { return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.match = function (pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; var substr = this.string.substr(this.pos, pattern.length); if (cased(substr) == cased(pattern)) { if (consume !== false) { this.pos += pattern.length; } return true } } else { var match = this.string.slice(this.pos).match(pattern); if (match && match.index > 0) { return null } if (match && consume !== false) { this.pos += match[0].length; } return match } }; StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; StringStream.prototype.hideFirstChars = function (n, inner) { this.lineStart += n; try { return inner() } finally { this.lineStart -= n; } }; StringStream.prototype.lookAhead = function (n) { var oracle = this.lineOracle; return oracle && oracle.lookAhead(n) }; StringStream.prototype.baseToken = function () { var oracle = this.lineOracle; return oracle && oracle.baseToken(this.pos) }; var SavedContext = function(state, lookAhead) { this.state = state; this.lookAhead = lookAhead; }; var Context = function(doc, state, line, lookAhead) { this.state = state; this.doc = doc; this.line = line; this.maxLookAhead = lookAhead || 0; this.baseTokens = null; this.baseTokenPos = 1; }; Context.prototype.lookAhead = function (n) { var line = this.doc.getLine(this.line + n); if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } return line }; Context.prototype.baseToken = function (n) { var this$1 = this; if (!this.baseTokens) { return null } while (this.baseTokens[this.baseTokenPos] <= n) { this$1.baseTokenPos += 2; } var type = this.baseTokens[this.baseTokenPos + 1]; return {type: type && type.replace(/( |^)overlay .*/, ""), size: this.baseTokens[this.baseTokenPos] - n} }; Context.prototype.nextLine = function () { this.line++; if (this.maxLookAhead > 0) { this.maxLookAhead--; } }; Context.fromSaved = function (doc, saved, line) { if (saved instanceof SavedContext) { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } else { return new Context(doc, copyState(doc.mode, saved), line) } }; Context.prototype.save = function (copy) { var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state }; // Compute a style array (an array starting with a mode generation // -- for invalidation -- followed by pairs of end positions and // style strings), which is used to highlight the tokens on the // line. function highlightLine(cm, line, context, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). var st = [cm.state.modeGen], lineClasses = {}; // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, lineClasses, forceToEnd); var state = context.state; // Run overlays, adjust style array. var loop = function ( o ) { context.baseTokens = st; var overlay = cm.state.overlays[o], i = 1, at = 0; context.state = true; runMode(cm, line.text, overlay.mode, context, function (end, style) { var start = i; // Ensure there's a token end at the current position, and that i points at it while (at < end) { var i_end = st[i]; if (i_end > end) { st.splice(i, 1, end, st[i+1], i_end); } i += 2; at = Math.min(end, i_end); } if (!style) { return } if (overlay.opaque) { st.splice(start, i - start, end, "overlay " + style); i = start + 2; } else { for (; start < i; start += 2) { var cur = st[start+1]; st[start+1] = (cur ? cur + " " : "") + "overlay " + style; } } }, lineClasses); context.state = state; context.baseTokens = null; context.baseTokenPos = 1; }; for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} } function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { var context = getContextBefore(cm, lineNo(line)); var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); var result = highlightLine(cm, line, context); if (resetState) { context.state = resetState; } line.stateAfter = context.save(!resetState); line.styles = result.styles; if (result.classes) { line.styleClasses = result.classes; } else if (line.styleClasses) { line.styleClasses = null; } if (updateFrontier === cm.doc.highlightFrontier) { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } } return line.styles } function getContextBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; if (!doc.mode.startState) { return new Context(doc, true, n) } var start = findStartLine(cm, n, precise); var saved = start > doc.first && getLine(doc, start - 1).stateAfter; var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); doc.iter(start, n, function (line) { processLine(cm, line.text, context); var pos = context.line; line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; context.nextLine(); }); if (precise) { doc.modeFrontier = context.line; } return context } // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. Used for lines that // aren't currently visible. function processLine(cm, text, context, startAt) { var mode = cm.doc.mode; var stream = new StringStream(text, cm.options.tabSize, context); stream.start = stream.pos = startAt || 0; if (text == "") { callBlankLine(mode, context.state); } while (!stream.eol()) { readToken(mode, stream, context.state); stream.start = stream.pos; } } function callBlankLine(mode, state) { if (mode.blankLine) { return mode.blankLine(state) } if (!mode.innerMode) { return } var inner = innerMode(mode, state); if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } } function readToken(mode, stream, state, inner) { for (var i = 0; i < 10; i++) { if (inner) { inner[0] = innerMode(mode, state).mode; } var style = mode.token(stream, state); if (stream.pos > stream.start) { return style } } throw new Error("Mode " + mode.name + " failed to advance stream.") } var Token = function(stream, type, state) { this.start = stream.start; this.end = stream.pos; this.string = stream.current(); this.type = type || null; this.state = state; }; // Utility for getTokenAt and getLineTokens function takeToken(cm, pos, precise, asArray) { var doc = cm.doc, mode = doc.mode, style; pos = clipPos(doc, pos); var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; if (asArray) { tokens = []; } while ((asArray || stream.pos < pos.ch) && !stream.eol()) { stream.start = stream.pos; style = readToken(mode, stream, context.state); if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } } return asArray ? tokens : new Token(stream, style, context.state) } function extractLineClasses(type, output) { if (type) { for (;;) { var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); if (!lineClass) { break } type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); var prop = lineClass[1] ? "bgClass" : "textClass"; if (output[prop] == null) { output[prop] = lineClass[2]; } else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) { output[prop] += " " + lineClass[2]; } } } return type } // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } var curStart = 0, curStyle = null; var stream = new StringStream(text, cm.options.tabSize, context), style; var inner = cm.options.addModeClass && [null]; if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; if (forceToEnd) { processLine(cm, text, context, stream.pos); } stream.pos = text.length; style = null; } else { style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); } if (inner) { var mName = inner[0].name; if (mName) { style = "m-" + (style ? mName + " " + style : mName); } } if (!flattenSpans || curStyle != style) { while (curStart < stream.start) { curStart = Math.min(stream.start, curStart + 5000); f(curStart, curStyle); } curStyle = style; } stream.start = stream.pos; } while (curStart < stream.pos) { // Webkit seems to refuse to render text nodes longer than 57444 // characters, and returns inaccurate measurements in nodes // starting around 5000 chars. var pos = Math.min(stream.pos, curStart + 5000); f(pos, curStyle); curStart = pos; } } // Finds the line to start with when starting a parse. Tries to // find a line with a stateAfter, so that it can start with a // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { var minindent, minline, doc = cm.doc; var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); for (var search = n; search > lim; --search) { if (search <= doc.first) { return doc.first } var line = getLine(doc, search - 1), after = line.stateAfter; if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) { return search } var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; minindent = indented; } } return minline } function retreatFrontier(doc, n) { doc.modeFrontier = Math.min(doc.modeFrontier, n); if (doc.highlightFrontier < n - 10) { return } var start = doc.first; for (var line = n - 1; line > start; line--) { var saved = getLine(doc, line).stateAfter; // change is on 3 // state on line 1 looked ahead 2 -- so saw 3 // test 1 + 2 < 3 should cover this if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { start = line + 1; break } } doc.highlightFrontier = Math.min(doc.highlightFrontier, start); } // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including // highlighting info (the styles array). var Line = function(text, markedSpans, estimateHeight) { this.text = text; attachMarkedSpans(this, markedSpans); this.height = estimateHeight ? estimateHeight(this) : 1; }; Line.prototype.lineNo = function () { return lineNo(this) }; eventMixin(Line); // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the // line's height. function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } if (line.order != null) { line.order = null; } detachMarkedSpans(line); attachMarkedSpans(line, markedSpans); var estHeight = estimateHeight ? estimateHeight(line) : 1; if (estHeight != line.height) { updateLineHeight(line, estHeight); } } // Detach a line from the document tree and its markers. function cleanUpLine(line) { line.parent = null; detachMarkedSpans(line); } // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. var styleToClassCache = {}, styleToClassCacheWithMode = {}; function interpretTokenStyle(style, options) { if (!style || /^\s*$/.test(style)) { return null } var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&")) } // Render the DOM representation of the text of a line. Also builds // up a 'line map', which points at the DOM nodes that represent // specific stretches of text, and is used by the measuring code. // The returned object contains the DOM node, this map, and // information about line-wide styles that were set by the mode. function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, splitSpaces: cm.getOption("lineWrapping")}; lineView.measure = {}; // Iterate over the logical lines that make up this visual line. for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); builder.pos = 0; builder.addToken = buildToken; // Optionally wire in some hacks into the token-rendering // algorithm, to deal with browser quirks. if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) { builder.addToken = buildTokenBadBidi(builder.addToken, order); } builder.map = []; var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); if (line.styleClasses) { if (line.styleClasses.bgClass) { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } if (line.styleClasses.textClass) { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } } // Ensure at least a single node is present, for measuring. if (builder.map.length == 0) { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } // Store the map and a cache object for the current logical line if (i == 0) { lineView.measure.map = builder.map; lineView.measure.cache = {}; } else { (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); } } // See issue #2901 if (webkit) { var last = builder.content.lastChild; if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) { builder.content.className = "cm-tab-wrap-hack"; } } signal(cm, "renderLine", cm, lineView.line, builder.pre); if (builder.pre.className) { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } return builder } function defaultSpecialCharPlaceholder(ch) { var token = elt("span", "\u2022", "cm-invalidchar"); token.title = "\\u" + ch.charCodeAt(0).toString(16); token.setAttribute("aria-label", token.title); return token } // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, title, css) { if (!text) { return } var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; var special = builder.cm.state.specialChars, mustWrap = false; var content; if (!special.test(text)) { builder.col += text.length; content = document.createTextNode(displayText); builder.map.push(builder.pos, builder.pos + text.length, content); if (ie && ie_version < 9) { mustWrap = true; } builder.pos += text.length; } else { content = document.createDocumentFragment(); var pos = 0; while (true) { special.lastIndex = pos; var m = special.exec(text); var skipped = m ? m.index - pos : text.length - pos; if (skipped) { var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } else { content.appendChild(txt); } builder.map.push(builder.pos, builder.pos + skipped, txt); builder.col += skipped; builder.pos += skipped; } if (!m) { break } pos += skipped + 1; var txt$1 = (void 0); if (m[0] == "\t") { var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); txt$1.setAttribute("role", "presentation"); txt$1.setAttribute("cm-text", "\t"); builder.col += tabWidth; } else if (m[0] == "\r" || m[0] == "\n") { txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); txt$1.setAttribute("cm-text", m[0]); builder.col += 1; } else { txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); txt$1.setAttribute("cm-text", m[0]); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } else { content.appendChild(txt$1); } builder.col += 1; } builder.map.push(builder.pos, builder.pos + 1, txt$1); builder.pos++; } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; if (style || startStyle || endStyle || mustWrap || css) { var fullStyle = style || ""; if (startStyle) { fullStyle += startStyle; } if (endStyle) { fullStyle += endStyle; } var token = elt("span", [content], fullStyle, css); if (title) { token.title = title; } return builder.content.appendChild(token) } builder.content.appendChild(content); } // Change some spaces to NBSP to prevent the browser from collapsing // trailing spaces at the end of a line when rendering text (issue #1362). function splitSpaces(text, trailingBefore) { if (text.length > 1 && !/ /.test(text)) { return text } var spaceBefore = trailingBefore, result = ""; for (var i = 0; i < text.length; i++) { var ch = text.charAt(i); if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) { ch = "\u00a0"; } result += ch; spaceBefore = ch == " "; } return result } // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { return function (builder, text, style, startStyle, endStyle, title, css) { style = style ? style + " cm-force-border" : "cm-force-border"; var start = builder.pos, end = start + text.length; for (;;) { // Find the part that overlaps with the start of this text var part = (void 0); for (var i = 0; i < order.length; i++) { part = order[i]; if (part.to > start && part.from <= start) { break } } if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) } inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); startStyle = null; text = text.slice(part.to - start); start = part.to; } } } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { var widget = !ignoreWidget && marker.widgetNode; if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) { widget = builder.content.appendChild(document.createElement("span")); } widget.setAttribute("cm-marker", marker.id); } if (widget) { builder.cm.display.input.setUneditable(widget); builder.content.appendChild(widget); } builder.pos += size; builder.trailingSpace = false; } // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i$1 = 1; i$1 < styles.length; i$1+=2) { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } return } var len = allText.length, pos = 0, i = 1, text = "", style, css; var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = title = css = ""; collapsed = null; nextChange = Infinity; var foundBookmarks = [], endStyles = (void 0); for (var j = 0; j < spans.length; ++j) { var sp = spans[j], m = sp.marker; if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { foundBookmarks.push(m); } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { if (sp.to != null && sp.to != pos && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } if (m.className) { spanStyle += " " + m.className; } if (m.css) { css = (css ? css + ";" : "") + m.css; } if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } if (m.title && !title) { title = m.title; } if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) { collapsed = sp; } } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; } } if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null); if (collapsed.to == null) { return } if (collapsed.to == pos) { collapsed = false; } } } if (pos >= len) { break } var upto = Math.min(len, nextChange); while (true) { if (text) { var end = pos + text.length; if (!collapsed) { var tokenText = end > upto ? text.slice(0, upto - pos) : text; builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} pos = end; spanStartStyle = ""; } text = allText.slice(at, at = styles[i++]); style = interpretTokenStyle(styles[i++], builder.cm.options); } } } // These objects are used to represent the visible (currently drawn) // part of the document. A LineView may correspond to multiple // logical lines, if those are connected by collapsed ranges. function LineView(doc, line, lineN) { // The starting line this.line = line; // Continuing lines, if any this.rest = visualLineContinued(line); // Number of logical lines in this visual line this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; this.node = this.text = null; this.hidden = lineIsHidden(doc, line); } // Create a range of LineView objects for the given lines. function buildViewArray(cm, from, to) { var array = [], nextPos; for (var pos = from; pos < to; pos = nextPos) { var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); nextPos = pos + view.size; array.push(view); } return array } var operationGroup = null; function pushOperation(op) { if (operationGroup) { operationGroup.ops.push(op); } else { op.ownsGroup = operationGroup = { ops: [op], delayedCallbacks: [] }; } } function fireCallbacksForOps(group) { // Calls delayed callbacks and cursorActivity handlers until no // new ones appear var callbacks = group.delayedCallbacks, i = 0; do { for (; i < callbacks.length; i++) { callbacks[i].call(null); } for (var j = 0; j < group.ops.length; j++) { var op = group.ops[j]; if (op.cursorActivityHandlers) { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } } } while (i < callbacks.length) } function finishOperation(op, endCb) { var group = op.ownsGroup; if (!group) { return } try { fireCallbacksForOps(group); } finally { operationGroup = null; endCb(group); } } var orphanDelayedCallbacks = null; // Often, we want to signal events at a point where we are in the // middle of some work, but don't want the handler to start calling // other methods on the editor, which might be in an inconsistent // state or simply not expect any other events to happen. // signalLater looks whether there are any handlers, and schedules // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. function signalLater(emitter, type /*, values...*/) { var arr = getHandlers(emitter, type); if (!arr.length) { return } var args = Array.prototype.slice.call(arguments, 2), list; if (operationGroup) { list = operationGroup.delayedCallbacks; } else if (orphanDelayedCallbacks) { list = orphanDelayedCallbacks; } else { list = orphanDelayedCallbacks = []; setTimeout(fireOrphanDelayed, 0); } var loop = function ( i ) { list.push(function () { return arr[i].apply(null, args); }); }; for (var i = 0; i < arr.length; ++i) loop( i ); } function fireOrphanDelayed() { var delayed = orphanDelayedCallbacks; orphanDelayedCallbacks = null; for (var i = 0; i < delayed.length; ++i) { delayed[i](); } } // When an aspect of a line changes, a string is added to // lineView.changes. This updates the relevant part of the line's // DOM structure. function updateLineForChanges(cm, lineView, lineN, dims) { for (var j = 0; j < lineView.changes.length; j++) { var type = lineView.changes[j]; if (type == "text") { updateLineText(cm, lineView); } else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } else if (type == "class") { updateLineClasses(cm, lineView); } else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } } lineView.changes = null; } // Lines with gutter elements, widgets or a background class need to // be wrapped, and have the extra elements added to the wrapper div function ensureLineWrapped(lineView) { if (lineView.node == lineView.text) { lineView.node = elt("div", null, null, "position: relative"); if (lineView.text.parentNode) { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } lineView.node.appendChild(lineView.text); if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } } return lineView.node } function updateLineBackground(cm, lineView) { var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; if (cls) { cls += " CodeMirror-linebackground"; } if (lineView.background) { if (cls) { lineView.background.className = cls; } else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } } else if (cls) { var wrap = ensureLineWrapped(lineView); lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); cm.display.input.setUneditable(lineView.background); } } // Wrapper around buildLineContent which will reuse the structure // in display.externalMeasured when possible. function getLineContent(cm, lineView) { var ext = cm.display.externalMeasured; if (ext && ext.line == lineView.line) { cm.display.externalMeasured = null; lineView.measure = ext.measure; return ext.built } return buildLineContent(cm, lineView) } // Redraw the line's text. Interacts with the background and text // classes because the mode may output tokens that influence these // classes. function updateLineText(cm, lineView) { var cls = lineView.text.className; var built = getLineContent(cm, lineView); if (lineView.text == lineView.node) { lineView.node = built.pre; } lineView.text.parentNode.replaceChild(built.pre, lineView.text); lineView.text = built.pre; if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { lineView.bgClass = built.bgClass; lineView.textClass = built.textClass; updateLineClasses(cm, lineView); } else if (cls) { lineView.text.className = cls; } } function updateLineClasses(cm, lineView) { updateLineBackground(cm, lineView); if (lineView.line.wrapClass) { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } else if (lineView.node != lineView.text) { lineView.node.className = ""; } var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; lineView.text.className = textClass || ""; } function updateLineGutter(cm, lineView, lineN, dims) { if (lineView.gutter) { lineView.node.removeChild(lineView.gutter); lineView.gutter = null; } if (lineView.gutterBackground) { lineView.node.removeChild(lineView.gutterBackground); lineView.gutterBackground = null; } if (lineView.line.gutterClass) { var wrap = ensureLineWrapped(lineView); lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(lineView.gutterBackground); wrap.insertBefore(lineView.gutterBackground, lineView.text); } var markers = lineView.line.gutterMarkers; if (cm.options.lineNumbers || markers) { var wrap$1 = ensureLineWrapped(lineView); var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(gutterWrap); wrap$1.insertBefore(gutterWrap, lineView.text); if (lineView.line.gutterClass) { gutterWrap.className += " " + lineView.line.gutterClass; } if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) { lineView.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) { var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; if (found) { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } } } } } function updateLineWidgets(cm, lineView, dims) { if (lineView.alignable) { lineView.alignable = null; } for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { next = node.nextSibling; if (node.className == "CodeMirror-linewidget") { lineView.node.removeChild(node); } } insertLineWidgets(cm, lineView, dims); } // Build a line's DOM representation from scratch function buildLineElement(cm, lineView, lineN, dims) { var built = getLineContent(cm, lineView); lineView.text = lineView.node = built.pre; if (built.bgClass) { lineView.bgClass = built.bgClass; } if (built.textClass) { lineView.textClass = built.textClass; } updateLineClasses(cm, lineView); updateLineGutter(cm, lineView, lineN, dims); insertLineWidgets(cm, lineView, dims); return lineView.node } // A lineView may contain multiple logical lines (when merged by // collapsed spans). The widgets for all of them need to be drawn. function insertLineWidgets(cm, lineView, dims) { insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } } function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { if (!line.widgets) { return } var wrap = ensureLineWrapped(lineView); for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } positionLineWidget(widget, node, lineView, dims); cm.display.input.setUneditable(node); if (allowAbove && widget.above) { wrap.insertBefore(node, lineView.gutter || lineView.text); } else { wrap.appendChild(node); } signalLater(widget, "redraw"); } } function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { (lineView.alignable || (lineView.alignable = [])).push(node); var width = dims.wrapperWidth; node.style.left = dims.fixedPos + "px"; if (!widget.coverGutter) { width -= dims.gutterTotalWidth; node.style.paddingLeft = dims.gutterTotalWidth + "px"; } node.style.width = width + "px"; } if (widget.coverGutter) { node.style.zIndex = 5; node.style.position = "relative"; if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } } } function widgetHeight(widget) { if (widget.height != null) { return widget.height } var cm = widget.doc.cm; if (!cm) { return 0 } if (!contains(document.body, widget.node)) { var parentStyle = "position: relative;"; if (widget.coverGutter) { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } if (widget.noHScroll) { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); } return widget.height = widget.node.parentNode.offsetHeight } // Return true when the given mouse event happened in a widget function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || (n.parentNode == display.sizer && n != display.mover)) { return true } } } // POSITION MEASUREMENT function paddingTop(display) {return display.lineSpace.offsetTop} function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} function paddingH(display) { if (display.cachedPaddingH) { return display.cachedPaddingH } var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } return data } function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } function displayWidth(cm) { return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth } function displayHeight(cm) { return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight } // Ensure the lineView.wrapping.heights array is populated. This is // an array of bottom offsets for the lines that make up a drawn // line. When lineWrapping is on, there might be more than one // height. function ensureLineHeights(cm, lineView, rect) { var wrapping = cm.options.lineWrapping; var curWidth = wrapping && displayWidth(cm); if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { var heights = lineView.measure.heights = []; if (wrapping) { lineView.measure.width = curWidth; var rects = lineView.text.firstChild.getClientRects(); for (var i = 0; i < rects.length - 1; i++) { var cur = rects[i], next = rects[i + 1]; if (Math.abs(cur.bottom - next.bottom) > 2) { heights.push((cur.bottom + next.top) / 2 - rect.top); } } } heights.push(rect.bottom - rect.top); } } // Find a line map (mapping character offsets to text nodes) and a // measurement cache for the given line number. (A line view might // contain multiple lines when collapsed ranges are present.) function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) { return {map: lineView.measure.map, cache: lineView.measure.cache} } for (var i = 0; i < lineView.rest.length; i++) { if (lineView.rest[i] == line) { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) { if (lineNo(lineView.rest[i$1]) > lineN) { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } } // Render a line into the hidden node display.externalMeasured. Used // when measurement is needed for a line that's not in the viewport. function updateExternalMeasurement(cm, line) { line = visualLine(line); var lineN = lineNo(line); var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); view.lineN = lineN; var built = view.built = buildLineContent(cm, view); view.text = built.pre; removeChildrenAndAdd(cm.display.lineMeasure, built.pre); return view } // Get a {top, bottom, left, right} box (in line-local coordinates) // for a given character. function measureChar(cm, line, ch, bias) { return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) } // Find a line view that corresponds to the given line number. function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) { return cm.display.view[findViewIndex(cm, lineN)] } var ext = cm.display.externalMeasured; if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) { return ext } } // Measurement can be split in two steps, the set-up work that // applies to the whole line, and the measurement of the actual // character. Functions like coordsChar, that need to do a lot of // measurements in a row, can thus ensure that the set-up work is // only done once. function prepareMeasureForLine(cm, line) { var lineN = lineNo(line); var view = findViewForLine(cm, lineN); if (view && !view.text) { view = null; } else if (view && view.changes) { updateLineForChanges(cm, view, lineN, getDimensions(cm)); cm.curOp.forceUpdate = true; } if (!view) { view = updateExternalMeasurement(cm, line); } var info = mapFromLineView(view, line, lineN); return { line: line, view: view, rect: null, map: info.map, cache: info.cache, before: info.before, hasHeights: false } } // Given a prepared measurement object, measures the position of an // actual character (or fetches it from the cache). function measureCharPrepared(cm, prepared, ch, bias, varHeight) { if (prepared.before) { ch = -1; } var key = ch + (bias || ""), found; if (prepared.cache.hasOwnProperty(key)) { found = prepared.cache[key]; } else { if (!prepared.rect) { prepared.rect = prepared.view.text.getBoundingClientRect(); } if (!prepared.hasHeights) { ensureLineHeights(cm, prepared.view, prepared.rect); prepared.hasHeights = true; } found = measureCharInner(cm, prepared, ch, bias); if (!found.bogus) { prepared.cache[key] = found; } } return {left: found.left, right: found.right, top: varHeight ? found.rtop : found.top, bottom: varHeight ? found.rbottom : found.bottom} } var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; function nodeAndOffsetInLineMap(map$$1, ch, bias) { var node, start, end, collapse, mStart, mEnd; // First, search the line map for the text node corresponding to, // or closest to, the target character. for (var i = 0; i < map$$1.length; i += 3) { mStart = map$$1[i]; mEnd = map$$1[i + 1]; if (ch < mStart) { start = 0; end = 1; collapse = "left"; } else if (ch < mEnd) { start = ch - mStart; end = start + 1; } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) { end = mEnd - mStart; start = end - 1; if (ch >= mEnd) { collapse = "right"; } } if (start != null) { node = map$$1[i + 2]; if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) { collapse = bias; } if (bias == "left" && start == 0) { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) { node = map$$1[(i -= 3) + 2]; collapse = "left"; } } if (bias == "right" && start == mEnd - mStart) { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) { node = map$$1[(i += 3) + 2]; collapse = "right"; } } break } } return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} } function getUsefulRect(rects, bias) { var rect = nullRect; if (bias == "left") { for (var i = 0; i < rects.length; i++) { if ((rect = rects[i]).left != rect.right) { break } } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { if ((rect = rects[i$1]).left != rect.right) { break } } } return rect } function measureCharInner(cm, prepared, ch, bias) { var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); var node = place.node, start = place.start, end = place.end, collapse = place.collapse; var rect; if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) { rect = node.parentNode.getBoundingClientRect(); } else { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } if (rect.left || rect.right || start == 0) { break } end = start; start = start - 1; collapse = "right"; } if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } } else { // If it is a widget, simply get the box for the whole widget. if (start > 0) { collapse = bias = "right"; } var rects; if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) { rect = rects[bias == "right" ? rects.length - 1 : 0]; } else { rect = node.getBoundingClientRect(); } } if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { var rSpan = node.parentNode.getClientRects()[0]; if (rSpan) { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } else { rect = nullRect; } } var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; var mid = (rtop + rbot) / 2; var heights = prepared.view.measure.heights; var i = 0; for (; i < heights.length - 1; i++) { if (mid < heights[i]) { break } } var top = i ? heights[i - 1] : 0, bot = heights[i]; var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, top: top, bottom: bot}; if (!rect.left && !rect.right) { result.bogus = true; } if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } return result } // Work around problem with bounding client rects on ranges being // returned incorrectly when zoomed on IE10 and below. function maybeUpdateRectForZooming(measure, rect) { if (!window.screen || screen.logicalXDPI == null || screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) { return rect } var scaleX = screen.logicalXDPI / screen.deviceXDPI; var scaleY = screen.logicalYDPI / screen.deviceYDPI; return {left: rect.left * scaleX, right: rect.right * scaleX, top: rect.top * scaleY, bottom: rect.bottom * scaleY} } function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { lineView.measure.cache = {}; lineView.measure.heights = null; if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { lineView.measure.caches[i] = {}; } } } } function clearLineMeasurementCache(cm) { cm.display.externalMeasure = null; removeChildren(cm.display.lineMeasure); for (var i = 0; i < cm.display.view.length; i++) { clearLineMeasurementCacheFor(cm.display.view[i]); } } function clearCaches(cm) { clearLineMeasurementCache(cm); cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } cm.display.lineNumChars = null; } function pageScrollX() { // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 // which causes page_Offset and bounding client rects to use // different reference viewports and invalidate our calculations. if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } return window.pageXOffset || (document.documentElement || document.body).scrollLeft } function pageScrollY() { if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } return window.pageYOffset || (document.documentElement || document.body).scrollTop } function widgetTopHeight(lineObj) { var height = 0; if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) { height += widgetHeight(lineObj.widgets[i]); } } } return height } // Converts a {top, bottom, left, right} box from line-local // coordinates into another coordinate system. Context may be one of // "line", "div" (display.lineDiv), "local"./null (editor), "window", // or "page". function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { if (!includeWidgets) { var height = widgetTopHeight(lineObj); rect.top += height; rect.bottom += height; } if (context == "line") { return rect } if (!context) { context = "local"; } var yOff = heightAtLine(lineObj); if (context == "local") { yOff += paddingTop(cm.display); } else { yOff -= cm.display.viewOffset; } if (context == "page" || context == "window") { var lOff = cm.display.lineSpace.getBoundingClientRect(); yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; return rect } // Coverts a box from "div" coords to another coordinate system. // Context may be "window", "page", "div", or "local"./null. function fromCoordSystem(cm, coords, context) { if (context == "div") { return coords } var left = coords.left, top = coords.top; // First move into "page" coordinate system if (context == "page") { left -= pageScrollX(); top -= pageScrollY(); } else if (context == "local" || !context) { var localBox = cm.display.sizer.getBoundingClientRect(); left += localBox.left; top += localBox.top; } var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} } function charCoords(cm, pos, context, lineObj, bias) { if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) } // Returns a box for a given cursor position, which may have an // 'other' property containing the position of the secondary cursor // on a bidi boundary. // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` // and after `char - 1` in writing order of `char - 1` // A cursor Pos(line, char, "after") is on the same visual line as `char` // and before `char` in writing order of `char` // Examples (upper-case letters are RTL, lower-case are LTR): // Pos(0, 1, ...) // before after // ab a|b a|b // aB a|B aB| // Ab |Ab A|b // AB B|A B|A // Every position after the last character on a line is considered to stick // to the last character on the line. function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { lineObj = lineObj || getLine(cm.doc, pos.line); if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } function get(ch, right) { var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); if (right) { m.left = m.right; } else { m.right = m.left; } return intoCoordSystem(cm, lineObj, m, context) } var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; if (ch >= lineObj.text.length) { ch = lineObj.text.length; sticky = "before"; } else if (ch <= 0) { ch = 0; sticky = "after"; } if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } function getBidi(ch, partPos, invert) { var part = order[partPos], right = part.level == 1; return get(invert ? ch - 1 : ch, right != invert) } var partPos = getBidiPartAt(order, ch, sticky); var other = bidiOther; var val = getBidi(ch, partPos, sticky == "before"); if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } return val } // Used to cheaply estimate the coordinates for a position. Used for // intermediate scroll updates. function estimateCoords(cm, pos) { var left = 0; pos = clipPos(cm.doc, pos); if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } var lineObj = getLine(cm.doc, pos.line); var top = heightAtLine(lineObj) + paddingTop(cm.display); return {left: left, right: left, top: top, bottom: top + lineObj.height} } // Positions returned by coordsChar contain some extra information. // xRel is the relative x position of the input coordinates compared // to the found position (so xRel > 0 means the coordinates are to // the right of the character position, for example). When outside // is true, that means the coordinates lie outside the line's // vertical range. function PosWithInfo(line, ch, sticky, outside, xRel) { var pos = Pos(line, ch, sticky); pos.xRel = xRel; if (outside) { pos.outside = true; } return pos } // Compute the character position closest to the given coordinates. // Input must be lineSpace-local ("div" coordinate system). function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) } var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineN > last) { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) } if (x < 0) { x = 0; } var lineObj = getLine(doc, lineN); for (;;) { var found = coordsCharInner(cm, lineObj, lineN, x, y); var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0)); if (!collapsed) { return found } var rangeEnd = collapsed.find(1); if (rangeEnd.line == lineN) { return rangeEnd } lineObj = getLine(doc, lineN = rangeEnd.line); } } function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { y -= widgetTopHeight(lineObj); var end = lineObj.text.length; var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); return {begin: begin, end: end} } function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) } // Returns true if the given side of a box is after the given // coordinates, in top-to-bottom, left-to-right order. function boxIsAfter(box, x, y, left) { return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x } function coordsCharInner(cm, lineObj, lineNo$$1, x, y) { // Move y into line-local coordinate space y -= heightAtLine(lineObj); var preparedMeasure = prepareMeasureForLine(cm, lineObj); // When directly calling `measureCharPrepared`, we have to adjust // for the widgets at this line. var widgetHeight$$1 = widgetTopHeight(lineObj); var begin = 0, end = lineObj.text.length, ltr = true; var order = getOrder(lineObj, cm.doc.direction); // If the line isn't plain left-to-right text, first figure out // which bidi section the coordinates fall into. if (order) { var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y); ltr = part.level != 1; // The awkward -1 offsets are needed because findFirst (called // on these below) will treat its first bound as inclusive, // second as exclusive, but we want to actually address the // characters in the part's range begin = ltr ? part.from : part.to - 1; end = ltr ? part.to : part.from - 1; } // A binary search to find the first character whose bounding box // starts after the coordinates. If we run across any whose box wrap // the coordinates, store that. var chAround = null, boxAround = null; var ch = findFirst(function (ch) { var box = measureCharPrepared(cm, preparedMeasure, ch); box.top += widgetHeight$$1; box.bottom += widgetHeight$$1; if (!boxIsAfter(box, x, y, false)) { return false } if (box.top <= y && box.left <= x) { chAround = ch; boxAround = box; } return true }, begin, end); var baseX, sticky, outside = false; // If a box around the coordinates was found, use that if (boxAround) { // Distinguish coordinates nearer to the left or right side of the box var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; ch = chAround + (atStart ? 0 : 1); sticky = atStart ? "after" : "before"; baseX = atLeft ? boxAround.left : boxAround.right; } else { // (Adjust for extended bound, if necessary.) if (!ltr && (ch == end || ch == begin)) { ch++; } // To determine which side to associate with, get the box to the // left of the character and compare it's vertical position to the // coordinates sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ? "after" : "before"; // Now get accurate coordinates for this place, in order to get a // base X position var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure); baseX = coords.left; outside = y < coords.top || y >= coords.bottom; } ch = skipExtendingChars(lineObj.text, ch, 1); return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX) } function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) { // Bidi parts are sorted left-to-right, and in a non-line-wrapping // situation, we can take this ordering to correspond to the visual // ordering. This finds the first part whose end is after the given // coordinates. var index = findFirst(function (i) { var part = order[i], ltr = part.level != 1; return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"), "line", lineObj, preparedMeasure), x, y, true) }, 0, order.length - 1); var part = order[index]; // If this isn't the first part, the part's start is also after // the coordinates, and the coordinates aren't on the same line as // that start, move one part back. if (index > 0) { var ltr = part.level != 1; var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"), "line", lineObj, preparedMeasure); if (boxIsAfter(start, x, y, true) && start.top > y) { part = order[index - 1]; } } return part } function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { // In a wrapped line, rtl text on wrapping boundaries can do things // that don't correspond to the ordering in our `order` array at // all, so a binary search doesn't work, and we want to return a // part that only spans one line so that the binary search in // coordsCharInner is safe. As such, we first find the extent of the // wrapped line, and then do a flat search in which we discard any // spans that aren't on the line. var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); var begin = ref.begin; var end = ref.end; if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } var part = null, closestDist = null; for (var i = 0; i < order.length; i++) { var p = order[i]; if (p.from >= end || p.to <= begin) { continue } var ltr = p.level != 1; var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; // Weigh against spans ending before this, so that they are only // picked if nothing ends after var dist = endX < x ? x - endX + 1e9 : endX - x; if (!part || closestDist > dist) { part = p; closestDist = dist; } } if (!part) { part = order[order.length - 1]; } // Clip the part to the wrapped line. if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } return part } var measureText; // Compute the default text height. function textHeight(display) { if (display.cachedTextHeight != null) { return display.cachedTextHeight } if (measureText == null) { measureText = elt("pre"); // Measure a bunch of lines, for browsers that compute // fractional heights. for (var i = 0; i < 49; ++i) { measureText.appendChild(document.createTextNode("x")); measureText.appendChild(elt("br")); } measureText.appendChild(document.createTextNode("x")); } removeChildrenAndAdd(display.measure, measureText); var height = measureText.offsetHeight / 50; if (height > 3) { display.cachedTextHeight = height; } removeChildren(display.measure); return height || 1 } // Compute the default character width. function charWidth(display) { if (display.cachedCharWidth != null) { return display.cachedCharWidth } var anchor = elt("span", "xxxxxxxxxx"); var pre = elt("pre", [anchor]); removeChildrenAndAdd(display.measure, pre); var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; if (width > 2) { display.cachedCharWidth = width; } return width || 10 } // Do a bulk-read of the DOM positions and sizes needed to draw the // view, so that we don't interleave reading and writing to the DOM. function getDimensions(cm) { var d = cm.display, left = {}, width = {}; var gutterLeft = d.gutters.clientLeft; for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; width[cm.options.gutters[i]] = n.clientWidth; } return {fixedPos: compensateForHScroll(d), gutterTotalWidth: d.gutters.offsetWidth, gutterLeft: left, gutterWidth: width, wrapperWidth: d.wrapper.clientWidth} } // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, // but using getBoundingClientRect to get a sub-pixel-accurate // result. function compensateForHScroll(display) { return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left } // Returns a function that estimates the height of a line, to use as // first approximation until the line becomes visible (and is thus // properly measurable). function estimateHeight(cm) { var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); return function (line) { if (lineIsHidden(cm.doc, line)) { return 0 } var widgetsHeight = 0; if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } } } if (wrapping) { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } else { return widgetsHeight + th } } } function estimateLineHeights(cm) { var doc = cm.doc, est = estimateHeight(cm); doc.iter(function (line) { var estHeight = est(line); if (estHeight != line.height) { updateLineHeight(line, estHeight); } }); } // Given a mouse event, find the corresponding position. If liberal // is false, it checks whether a gutter or scrollbar was clicked, // and returns null if it was. forRect is used by rectangular // selections, and tries to estimate a character position even for // coordinates beyond the right of the text. function posFromMouse(cm, e, liberal, forRect) { var display = cm.display; if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } var x, y, space = display.lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top; } catch (e) { return null } var coords = coordsChar(cm, x, y), line; if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); } return coords } // Find the view element corresponding to a given line. Return null // when the line isn't visible. function findViewIndex(cm, n) { if (n >= cm.display.viewTo) { return null } n -= cm.display.viewFrom; if (n < 0) { return null } var view = cm.display.view; for (var i = 0; i < view.length; i++) { n -= view[i].size; if (n < 0) { return i } } } function updateSelection(cm) { cm.display.input.showSelection(cm.display.input.prepareSelection()); } function prepareSelection(cm, primary) { if ( primary === void 0 ) primary = true; var doc = cm.doc, result = {}; var curFragment = result.cursors = document.createDocumentFragment(); var selFragment = result.selection = document.createDocumentFragment(); for (var i = 0; i < doc.sel.ranges.length; i++) { if (!primary && i == doc.sel.primIndex) { continue } var range$$1 = doc.sel.ranges[i]; if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue } var collapsed = range$$1.empty(); if (collapsed || cm.options.showCursorWhenSelecting) { drawSelectionCursor(cm, range$$1.head, curFragment); } if (!collapsed) { drawSelectionRange(cm, range$$1, selFragment); } } return result } // Draws a cursor for the given range function drawSelectionCursor(cm, head, output) { var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); cursor.style.left = pos.left + "px"; cursor.style.top = pos.top + "px"; cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); otherCursor.style.display = ""; otherCursor.style.left = pos.other.left + "px"; otherCursor.style.top = pos.other.top + "px"; otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; } } function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } // Draws the given range as a highlighted selection function drawSelectionRange(cm, range$$1, output) { var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var padding = paddingH(cm.display), leftSide = padding.left; var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; var docLTR = doc.direction == "ltr"; function add(left, top, width, bottom) { if (top < 0) { top = 0; } top = Math.round(top); bottom = Math.round(bottom); fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); } function drawForLine(line, fromArg, toArg) { var lineObj = getLine(doc, line); var lineLen = lineObj.text.length; var start, end; function coords(ch, bias) { return charCoords(cm, Pos(line, ch), "div", lineObj, bias) } function wrapX(pos, dir, side) { var extent = wrappedLineExtentChar(cm, lineObj, null, pos); var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); return coords(ch, prop)[prop] } var order = getOrder(lineObj, doc.direction); iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { var ltr = dir == "ltr"; var fromPos = coords(from, ltr ? "left" : "right"); var toPos = coords(to - 1, ltr ? "right" : "left"); var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; var first = i == 0, last = !order || i == order.length - 1; if (toPos.top - fromPos.top <= 3) { // Single line var openLeft = (docLTR ? openStart : openEnd) && first; var openRight = (docLTR ? openEnd : openStart) && last; var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; add(left, fromPos.top, right - left, fromPos.bottom); } else { // Multiple lines var topLeft, topRight, botLeft, botRight; if (ltr) { topLeft = docLTR && openStart && first ? leftSide : fromPos.left; topRight = docLTR ? rightSide : wrapX(from, dir, "before"); botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); botRight = docLTR && openEnd && last ? rightSide : toPos.right; } else { topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); topRight = !docLTR && openStart && first ? rightSide : fromPos.right; botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); } add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); } if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } if (cmpCoords(toPos, start) < 0) { start = toPos; } if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } if (cmpCoords(toPos, end) < 0) { end = toPos; } }); return {start: start, end: end} } var sFrom = range$$1.from(), sTo = range$$1.to(); if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); var singleVLine = visualLine(fromLine) == visualLine(toLine); var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); } else { add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); } } if (leftEnd.bottom < rightStart.top) { add(leftSide, leftEnd.bottom, null, rightStart.top); } } output.appendChild(fragment); } // Cursor-blinking function restartBlink(cm) { if (!cm.state.focused) { return } var display = cm.display; clearInterval(display.blinker); var on = true; display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } else if (cm.options.cursorBlinkRate < 0) { display.cursorDiv.style.visibility = "hidden"; } } function ensureFocus(cm) { if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } } function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true; setTimeout(function () { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; onBlur(cm); } }, 100); } function onFocus(cm, e) { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } if (cm.options.readOnly == "nocursor") { return } if (!cm.state.focused) { signal(cm, "focus", cm, e); cm.state.focused = true; addClass(cm.display.wrapper, "CodeMirror-focused"); // This test prevents this from firing when a context // menu is closed (since the input reset would kill the // select-all detection hack) if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { cm.display.input.reset(); if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 } cm.display.input.receivedFocus(); } restartBlink(cm); } function onBlur(cm, e) { if (cm.state.delayingBlurEvent) { return } if (cm.state.focused) { signal(cm, "blur", cm, e); cm.state.focused = false; rmClass(cm.display.wrapper, "CodeMirror-focused"); } clearInterval(cm.display.blinker); setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); } // Read the actual heights of the rendered lines, and update their // stored heights to match. function updateHeightsInViewport(cm) { var display = cm.display; var prevBottom = display.lineDiv.offsetTop; for (var i = 0; i < display.view.length; i++) { var cur = display.view[i], height = (void 0); if (cur.hidden) { continue } if (ie && ie_version < 8) { var bot = cur.node.offsetTop + cur.node.offsetHeight; height = bot - prevBottom; prevBottom = bot; } else { var box = cur.node.getBoundingClientRect(); height = box.bottom - box.top; } var diff = cur.line.height - height; if (height < 2) { height = textHeight(display); } if (diff > .005 || diff < -.005) { updateLineHeight(cur.line, height); updateWidgetHeight(cur.line); if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) { updateWidgetHeight(cur.rest[j]); } } } } } // Read and store the height of line widgets associated with the // given line. function updateWidgetHeight(line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { var w = line.widgets[i], parent = w.node.parentNode; if (parent) { w.height = parent.offsetHeight; } } } } // Compute the lines that are visible in a given viewport (defaults // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. function visibleLines(display, doc, viewport) { var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; top = Math.floor(top - paddingTop(display)); var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); // Ensure is a {from: {line, ch}, to: {line, ch}} object, and // forces those lines into the viewport (if possible). if (viewport && viewport.ensure) { var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; if (ensureFrom < from) { from = ensureFrom; to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); } else if (Math.min(ensureTo, doc.lastLine()) >= to) { from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); to = ensureTo; } } return {from: from, to: Math.max(to, from + 1)} } // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. function alignHorizontally(cm) { var display = cm.display, view = display.view; if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; var gutterW = display.gutters.offsetWidth, left = comp + "px"; for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { if (cm.options.fixedGutter) { if (view[i].gutter) { view[i].gutter.style.left = left; } if (view[i].gutterBackground) { view[i].gutterBackground.style.left = left; } } var align = view[i].alignable; if (align) { for (var j = 0; j < align.length; j++) { align[j].style.left = left; } } } } if (cm.options.fixedGutter) { display.gutters.style.left = (comp + gutterW) + "px"; } } // Used to ensure that the line number gutter is still the right // size for the current document size. Returns true when an update // is needed. function maybeUpdateLineNumberWidth(cm) { if (!cm.options.lineNumbers) { return false } var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; if (last.length != display.lineNumChars) { var test = display.measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; display.lineGutter.style.width = ""; display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; display.lineNumWidth = display.lineNumInnerWidth + padding; display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; display.lineGutter.style.width = display.lineNumWidth + "px"; updateGutterSpace(cm); return true } return false } // SCROLLING THINGS INTO VIEW // If an editor sits on the top or bottom of the window, partially // scrolled out of view, this ensures that the cursor is visible. function maybeScrollWindow(cm, rect) { if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; if (rect.top + box.top < 0) { doScroll = true; } else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } if (doScroll != null && !phantom) { var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); cm.display.lineSpace.appendChild(scrollNode); scrollNode.scrollIntoView(doScroll); cm.display.lineSpace.removeChild(scrollNode); } } // Scroll a given position into view (immediately), verifying that // it actually became visible (as line heights are accurately // measured, the position of something may 'drift' during drawing). function scrollPosIntoView(cm, pos, end, margin) { if (margin == null) { margin = 0; } var rect; if (!cm.options.lineWrapping && pos == end) { // Set pos and end to the cursor positions around the character pos sticks to // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch // If pos == Pos(_, 0, "before"), pos and end are unchanged pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; } for (var limit = 0; limit < 5; limit++) { var changed = false; var coords = cursorCoords(cm, pos); var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); rect = {left: Math.min(coords.left, endCoords.left), top: Math.min(coords.top, endCoords.top) - margin, right: Math.max(coords.left, endCoords.left), bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; var scrollPos = calculateScrollPos(cm, rect); var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } } if (!changed) { break } } return rect } // Scroll a given set of coordinates into view (immediately). function scrollIntoView(cm, rect) { var scrollPos = calculateScrollPos(cm, rect); if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } } // Calculate a new scroll position needed to scroll the given // rectangle into view. Returns an object with scrollTop and // scrollLeft properties. When these are undefined, the // vertical/horizontal position does not need to be adjusted. function calculateScrollPos(cm, rect) { var display = cm.display, snapMargin = textHeight(cm.display); if (rect.top < 0) { rect.top = 0; } var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; var screen = displayHeight(cm), result = {}; if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } var docBottom = cm.doc.height + paddingVert(display); var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; if (rect.top < screentop) { result.scrollTop = atTop ? 0 : rect.top; } else if (rect.bottom > screentop + screen) { var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); if (newTop != screentop) { result.scrollTop = newTop; } } var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); var tooWide = rect.right - rect.left > screenw; if (tooWide) { rect.right = rect.left + screenw; } if (rect.left < 10) { result.scrollLeft = 0; } else if (rect.left < screenleft) { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } else if (rect.right > screenw + screenleft - 3) { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } return result } // Store a relative adjustment to the scroll position in the current // operation (to be applied when the operation finishes). function addToScrollTop(cm, top) { if (top == null) { return } resolveScrollToPos(cm); cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; } // Make sure that at the end of the operation the current cursor is // shown. function ensureCursorVisible(cm) { resolveScrollToPos(cm); var cur = cm.getCursor(); cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; } function scrollToCoords(cm, x, y) { if (x != null || y != null) { resolveScrollToPos(cm); } if (x != null) { cm.curOp.scrollLeft = x; } if (y != null) { cm.curOp.scrollTop = y; } } function scrollToRange(cm, range$$1) { resolveScrollToPos(cm); cm.curOp.scrollToPos = range$$1; } // When an operation has its scrollToPos property set, and another // scroll action is applied before the end of the operation, this // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. function resolveScrollToPos(cm) { var range$$1 = cm.curOp.scrollToPos; if (range$$1) { cm.curOp.scrollToPos = null; var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to); scrollToCoordsRange(cm, from, to, range$$1.margin); } } function scrollToCoordsRange(cm, from, to, margin) { var sPos = calculateScrollPos(cm, { left: Math.min(from.left, to.left), top: Math.min(from.top, to.top) - margin, right: Math.max(from.right, to.right), bottom: Math.max(from.bottom, to.bottom) + margin }); scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); } // Sync the scrollable area and scrollbars, ensure the viewport // covers the visible area. function updateScrollTop(cm, val) { if (Math.abs(cm.doc.scrollTop - val) < 2) { return } if (!gecko) { updateDisplaySimple(cm, {top: val}); } setScrollTop(cm, val, true); if (gecko) { updateDisplaySimple(cm); } startWorker(cm, 100); } function setScrollTop(cm, val, forceScroll) { val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); if (cm.display.scroller.scrollTop == val && !forceScroll) { return } cm.doc.scrollTop = val; cm.display.scrollbars.setScrollTop(val); if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } } // Sync scroller and scrollbar, ensure the gutter elements are // aligned. function setScrollLeft(cm, val, isScroller, forceScroll) { val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } cm.doc.scrollLeft = val; alignHorizontally(cm); if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } cm.display.scrollbars.setScrollLeft(val); } // SCROLLBARS // Prepare DOM reads needed to update the scrollbars. Done in one // shot to minimize update/measure roundtrips. function measureForScrollbars(cm) { var d = cm.display, gutterW = d.gutters.offsetWidth; var docH = Math.round(cm.doc.height + paddingVert(cm.display)); return { clientHeight: d.scroller.clientHeight, viewHeight: d.wrapper.clientHeight, scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, viewWidth: d.wrapper.clientWidth, barLeft: cm.options.fixedGutter ? gutterW : 0, docHeight: docH, scrollHeight: docH + scrollGap(cm) + d.barHeight, nativeBarWidth: d.nativeBarWidth, gutterWidth: gutterW } } var NativeScrollbars = function(place, scroll, cm) { this.cm = cm; var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); vert.tabIndex = horiz.tabIndex = -1; place(vert); place(horiz); on(vert, "scroll", function () { if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } }); on(horiz, "scroll", function () { if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } }); this.checkedZeroWidth = false; // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } }; NativeScrollbars.prototype.update = function (measure) { var needsH = measure.scrollWidth > measure.clientWidth + 1; var needsV = measure.scrollHeight > measure.clientHeight + 1; var sWidth = measure.nativeBarWidth; if (needsV) { this.vert.style.display = "block"; this.vert.style.bottom = needsH ? sWidth + "px" : "0"; var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); // A bug in IE8 can cause this value to be negative, so guard it. this.vert.firstChild.style.height = Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; } else { this.vert.style.display = ""; this.vert.firstChild.style.height = "0"; } if (needsH) { this.horiz.style.display = "block"; this.horiz.style.right = needsV ? sWidth + "px" : "0"; this.horiz.style.left = measure.barLeft + "px"; var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); this.horiz.firstChild.style.width = Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; } else { this.horiz.style.display = ""; this.horiz.firstChild.style.width = "0"; } if (!this.checkedZeroWidth && measure.clientHeight > 0) { if (sWidth == 0) { this.zeroWidthHack(); } this.checkedZeroWidth = true; } return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} }; NativeScrollbars.prototype.setScrollLeft = function (pos) { if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } }; NativeScrollbars.prototype.setScrollTop = function (pos) { if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } }; NativeScrollbars.prototype.zeroWidthHack = function () { var w = mac && !mac_geMountainLion ? "12px" : "18px"; this.horiz.style.height = this.vert.style.width = w; this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; this.disableHoriz = new Delayed; this.disableVert = new Delayed; }; NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { bar.style.pointerEvents = "auto"; function maybeDisable() { // To find out whether the scrollbar is still visible, we // check whether the element under the pixel in the bottom // right corner of the scrollbar box is the scrollbar box // itself (when the bar is still visible) or its filler child // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. var box = bar.getBoundingClientRect(); var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); if (elt$$1 != bar) { bar.style.pointerEvents = "none"; } else { delay.set(1000, maybeDisable); } } delay.set(1000, maybeDisable); }; NativeScrollbars.prototype.clear = function () { var parent = this.horiz.parentNode; parent.removeChild(this.horiz); parent.removeChild(this.vert); }; var NullScrollbars = function () {}; NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; NullScrollbars.prototype.setScrollLeft = function () {}; NullScrollbars.prototype.setScrollTop = function () {}; NullScrollbars.prototype.clear = function () {}; function updateScrollbars(cm, measure) { if (!measure) { measure = measureForScrollbars(cm); } var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; updateScrollbarsInner(cm, measure); for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { if (startWidth != cm.display.barWidth && cm.options.lineWrapping) { updateHeightsInViewport(cm); } updateScrollbarsInner(cm, measureForScrollbars(cm)); startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; } } // Re-synchronize the fake scrollbars with the actual size of the // content. function updateScrollbarsInner(cm, measure) { var d = cm.display; var sizes = d.scrollbars.update(measure); d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; if (sizes.right && sizes.bottom) { d.scrollbarFiller.style.display = "block"; d.scrollbarFiller.style.height = sizes.bottom + "px"; d.scrollbarFiller.style.width = sizes.right + "px"; } else { d.scrollbarFiller.style.display = ""; } if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { d.gutterFiller.style.display = "block"; d.gutterFiller.style.height = sizes.bottom + "px"; d.gutterFiller.style.width = measure.gutterWidth + "px"; } else { d.gutterFiller.style.display = ""; } } var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; function initScrollbars(cm) { if (cm.display.scrollbars) { cm.display.scrollbars.clear(); if (cm.display.scrollbars.addClass) { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); // Prevent clicks in the scrollbars from killing focus on(node, "mousedown", function () { if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } }); node.setAttribute("cm-not-content", "true"); }, function (pos, axis) { if (axis == "horizontal") { setScrollLeft(cm, pos); } else { updateScrollTop(cm, pos); } }, cm); if (cm.display.scrollbars.addClass) { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } // Operations are used to wrap a series of changes to the editor // state in such a way that each change won't have to update the // cursor and display (which would be awkward, slow, and // error-prone). Instead, display updates are batched and then all // combined and executed at once. var nextOpId = 0; // Start a new operation. function startOperation(cm) { cm.curOp = { cm: cm, viewChanged: false, // Flag that indicates that lines might need to be redrawn startHeight: cm.doc.height, // Used to detect need to update scrollbar forceUpdate: false, // Used to force a redraw updateInput: null, // Whether to reset the input textarea typing: false, // Whether this reset should be careful to leave existing text (for compositing) changeObjs: null, // Accumulated changes, for firing change events cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already selectionChanged: false, // Whether the selection needs to be redrawn updateMaxLine: false, // Set when the widest line needs to be determined anew scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet scrollToPos: null, // Used to scroll to a specific position focus: false, id: ++nextOpId // Unique ID }; pushOperation(cm.curOp); } // Finish an operation, updating the display and signalling delayed events function endOperation(cm) { var op = cm.curOp; if (op) { finishOperation(op, function (group) { for (var i = 0; i < group.ops.length; i++) { group.ops[i].cm.curOp = null; } endOperations(group); }); } } // The DOM updates done when an operation finishes are batched so // that the minimum number of relayouts are required. function endOperations(group) { var ops = group.ops; for (var i = 0; i < ops.length; i++) // Read DOM { endOperation_R1(ops[i]); } for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) { endOperation_W1(ops[i$1]); } for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM { endOperation_R2(ops[i$2]); } for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) { endOperation_W2(ops[i$3]); } for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM { endOperation_finish(ops[i$4]); } } function endOperation_R1(op) { var cm = op.cm, display = cm.display; maybeClipScrollbars(cm); if (op.updateMaxLine) { findMaxLine(cm); } op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || op.scrollToPos.to.line >= display.viewTo) || display.maxLineChanged && cm.options.lineWrapping; op.update = op.mustUpdate && new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); } function endOperation_W1(op) { op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); } function endOperation_R2(op) { var cm = op.cm, display = cm.display; if (op.updatedDisplay) { updateHeightsInViewport(cm); } op.barMeasure = measureForScrollbars(cm); // If the max line changed since it was last measured, measure it, // and ensure the document's width matches it. // updateDisplay_W2 will use these properties to do the actual resizing if (display.maxLineChanged && !cm.options.lineWrapping) { op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; cm.display.sizerWidth = op.adjustWidthTo; op.barMeasure.scrollWidth = Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); } if (op.updatedDisplay || op.selectionChanged) { op.preparedSelection = display.input.prepareSelection(); } } function endOperation_W2(op) { var cm = op.cm; if (op.adjustWidthTo != null) { cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; if (op.maxScrollLeft < cm.doc.scrollLeft) { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } cm.display.maxLineChanged = false; } var takeFocus = op.focus && op.focus == activeElt(); if (op.preparedSelection) { cm.display.input.showSelection(op.preparedSelection, takeFocus); } if (op.updatedDisplay || op.startHeight != cm.doc.height) { updateScrollbars(cm, op.barMeasure); } if (op.updatedDisplay) { setDocumentHeight(cm, op.barMeasure); } if (op.selectionChanged) { restartBlink(cm); } if (cm.state.focused && op.updateInput) { cm.display.input.reset(op.typing); } if (takeFocus) { ensureFocus(op.cm); } } function endOperation_finish(op) { var cm = op.cm, display = cm.display, doc = cm.doc; if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) { display.wheelStartX = display.wheelStartY = null; } // Propagate the scroll position to the actual DOM scroller if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); maybeScrollWindow(cm, rect); } // Fire events for markers that are hidden/unidden by editing or // undoing var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; if (hidden) { for (var i = 0; i < hidden.length; ++i) { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } if (display.wrapper.offsetHeight) { doc.scrollTop = cm.display.scroller.scrollTop; } // Fire change events, and delayed event handlers if (op.changeObjs) { signal(cm, "changes", cm, op.changeObjs); } if (op.update) { op.update.finish(); } } // Run the given function in an operation function runInOp(cm, f) { if (cm.curOp) { return f() } startOperation(cm); try { return f() } finally { endOperation(cm); } } // Wraps a function in an operation. Returns the wrapped function. function operation(cm, f) { return function() { if (cm.curOp) { return f.apply(cm, arguments) } startOperation(cm); try { return f.apply(cm, arguments) } finally { endOperation(cm); } } } // Used to add methods to editor and doc instances, wrapping them in // operations. function methodOp(f) { return function() { if (this.curOp) { return f.apply(this, arguments) } startOperation(this); try { return f.apply(this, arguments) } finally { endOperation(this); } } } function docMethodOp(f) { return function() { var cm = this.cm; if (!cm || cm.curOp) { return f.apply(this, arguments) } startOperation(cm); try { return f.apply(this, arguments) } finally { endOperation(cm); } } } // Updates the display.view data structure for a given change to the // document. From and to are in pre-change coordinates. Lendiff is // the amount of lines added or subtracted by the change. This is // used for changes that span multiple lines, or change the way // lines are divided into visual lines. regLineChange (below) // registers single-line changes. function regChange(cm, from, to, lendiff) { if (from == null) { from = cm.doc.first; } if (to == null) { to = cm.doc.first + cm.doc.size; } if (!lendiff) { lendiff = 0; } var display = cm.display; if (lendiff && to < display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers > from)) { display.updateLineNumbers = from; } cm.curOp.viewChanged = true; if (from >= display.viewTo) { // Change after if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) { resetView(cm); } } else if (to <= display.viewFrom) { // Change before if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { resetView(cm); } else { display.viewFrom += lendiff; display.viewTo += lendiff; } } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap resetView(cm); } else if (from <= display.viewFrom) { // Top overlap var cut = viewCuttingPoint(cm, to, to + lendiff, 1); if (cut) { display.view = display.view.slice(cut.index); display.viewFrom = cut.lineN; display.viewTo += lendiff; } else { resetView(cm); } } else if (to >= display.viewTo) { // Bottom overlap var cut$1 = viewCuttingPoint(cm, from, from, -1); if (cut$1) { display.view = display.view.slice(0, cut$1.index); display.viewTo = cut$1.lineN; } else { resetView(cm); } } else { // Gap in the middle var cutTop = viewCuttingPoint(cm, from, from, -1); var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); if (cutTop && cutBot) { display.view = display.view.slice(0, cutTop.index) .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) .concat(display.view.slice(cutBot.index)); display.viewTo += lendiff; } else { resetView(cm); } } var ext = display.externalMeasured; if (ext) { if (to < ext.lineN) { ext.lineN += lendiff; } else if (from < ext.lineN + ext.size) { display.externalMeasured = null; } } } // Register a change to a single line. Type must be one of "text", // "gutter", "class", "widget" function regLineChange(cm, line, type) { cm.curOp.viewChanged = true; var display = cm.display, ext = cm.display.externalMeasured; if (ext && line >= ext.lineN && line < ext.lineN + ext.size) { display.externalMeasured = null; } if (line < display.viewFrom || line >= display.viewTo) { return } var lineView = display.view[findViewIndex(cm, line)]; if (lineView.node == null) { return } var arr = lineView.changes || (lineView.changes = []); if (indexOf(arr, type) == -1) { arr.push(type); } } // Clear the view. function resetView(cm) { cm.display.viewFrom = cm.display.viewTo = cm.doc.first; cm.display.view = []; cm.display.viewOffset = 0; } function viewCuttingPoint(cm, oldN, newN, dir) { var index = findViewIndex(cm, oldN), diff, view = cm.display.view; if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) { return {index: index, lineN: newN} } var n = cm.display.viewFrom; for (var i = 0; i < index; i++) { n += view[i].size; } if (n != oldN) { if (dir > 0) { if (index == view.length - 1) { return null } diff = (n + view[index].size) - oldN; index++; } else { diff = n - oldN; } oldN += diff; newN += diff; } while (visualLineNo(cm.doc, newN) != newN) { if (index == (dir < 0 ? 0 : view.length - 1)) { return null } newN += dir * view[index - (dir < 0 ? 1 : 0)].size; index += dir; } return {index: index, lineN: newN} } // Force the view to cover a given range, adding empty view element // or clipping off existing ones as needed. function adjustView(cm, from, to) { var display = cm.display, view = display.view; if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { display.view = buildViewArray(cm, from, to); display.viewFrom = from; } else { if (display.viewFrom > from) { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } else if (display.viewFrom < from) { display.view = display.view.slice(findViewIndex(cm, from)); } display.viewFrom = from; if (display.viewTo < to) { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } else if (display.viewTo > to) { display.view = display.view.slice(0, findViewIndex(cm, to)); } } display.viewTo = to; } // Count the number of lines in the view whose DOM representation is // out of date (or nonexistent). function countDirtyView(cm) { var view = cm.display.view, dirty = 0; for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } } return dirty } // HIGHLIGHT WORKER function startWorker(cm, time) { if (cm.doc.highlightFrontier < cm.display.viewTo) { cm.state.highlight.set(time, bind(highlightWorker, cm)); } } function highlightWorker(cm) { var doc = cm.doc; if (doc.highlightFrontier >= cm.display.viewTo) { return } var end = +new Date + cm.options.workTime; var context = getContextBefore(cm, doc.highlightFrontier); var changedLines = []; doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { if (context.line >= cm.display.viewFrom) { // Visible var oldStyles = line.styles; var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; var highlighted = highlightLine(cm, line, context, true); if (resetState) { context.state = resetState; } line.styles = highlighted.styles; var oldCls = line.styleClasses, newCls = highlighted.classes; if (newCls) { line.styleClasses = newCls; } else if (oldCls) { line.styleClasses = null; } var ischange = !oldStyles || oldStyles.length != line.styles.length || oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } if (ischange) { changedLines.push(context.line); } line.stateAfter = context.save(); context.nextLine(); } else { if (line.text.length <= cm.options.maxHighlightLength) { processLine(cm, line.text, context); } line.stateAfter = context.line % 5 == 0 ? context.save() : null; context.nextLine(); } if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true } }); doc.highlightFrontier = context.line; doc.modeFrontier = Math.max(doc.modeFrontier, context.line); if (changedLines.length) { runInOp(cm, function () { for (var i = 0; i < changedLines.length; i++) { regLineChange(cm, changedLines[i], "text"); } }); } } // DISPLAY DRAWING var DisplayUpdate = function(cm, viewport, force) { var display = cm.display; this.viewport = viewport; // Store some values that we'll need later (but don't want to force a relayout for) this.visible = visibleLines(display, cm.doc, viewport); this.editorIsHidden = !display.wrapper.offsetWidth; this.wrapperHeight = display.wrapper.clientHeight; this.wrapperWidth = display.wrapper.clientWidth; this.oldDisplayWidth = displayWidth(cm); this.force = force; this.dims = getDimensions(cm); this.events = []; }; DisplayUpdate.prototype.signal = function (emitter, type) { if (hasHandler(emitter, type)) { this.events.push(arguments); } }; DisplayUpdate.prototype.finish = function () { var this$1 = this; for (var i = 0; i < this.events.length; i++) { signal.apply(null, this$1.events[i]); } }; function maybeClipScrollbars(cm) { var display = cm.display; if (!display.scrollbarsClipped && display.scroller.offsetWidth) { display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; display.heightForcer.style.height = scrollGap(cm) + "px"; display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; display.scrollbarsClipped = true; } } function selectionSnapshot(cm) { if (cm.hasFocus()) { return null } var active = activeElt(); if (!active || !contains(cm.display.lineDiv, active)) { return null } var result = {activeElt: active}; if (window.getSelection) { var sel = window.getSelection(); if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { result.anchorNode = sel.anchorNode; result.anchorOffset = sel.anchorOffset; result.focusNode = sel.focusNode; result.focusOffset = sel.focusOffset; } } return result } function restoreSelection(snapshot) { if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } snapshot.activeElt.focus(); if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { var sel = window.getSelection(), range$$1 = document.createRange(); range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset); range$$1.collapse(false); sel.removeAllRanges(); sel.addRange(range$$1); sel.extend(snapshot.focusNode, snapshot.focusOffset); } } // Does the actual updating of the line display. Bails out // (returning false) when there is nothing to be done and forced is // false. function updateDisplayIfNeeded(cm, update) { var display = cm.display, doc = cm.doc; if (update.editorIsHidden) { resetView(cm); return false } // Bail out if the visible area is already rendered and nothing changed. if (!update.force && update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && display.renderedView == display.view && countDirtyView(cm) == 0) { return false } if (maybeUpdateLineNumberWidth(cm)) { resetView(cm); update.dims = getDimensions(cm); } // Compute a suitable new viewport (from & to) var end = doc.first + doc.size; var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); var to = Math.min(end, update.visible.to + cm.options.viewportMargin); if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } if (sawCollapsedSpans) { from = visualLineNo(cm.doc, from); to = visualLineEndNo(cm.doc, to); } var different = from != display.viewFrom || to != display.viewTo || display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; adjustView(cm, from, to); display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); // Position the mover div to align with the current scroll position cm.display.mover.style.top = display.viewOffset + "px"; var toUpdate = countDirtyView(cm); if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) { return false } // For big changes, we hide the enclosing element during the // update, since that speeds up the operations on most browsers. var selSnapshot = selectionSnapshot(cm); if (toUpdate > 4) { display.lineDiv.style.display = "none"; } patchDisplay(cm, display.updateLineNumbers, update.dims); if (toUpdate > 4) { display.lineDiv.style.display = ""; } display.renderedView = display.view; // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. restoreSelection(selSnapshot); // Prevent selection and cursors from interfering with the scroll // width and height. removeChildren(display.cursorDiv); removeChildren(display.selectionDiv); display.gutters.style.height = display.sizer.style.minHeight = 0; if (different) { display.lastWrapHeight = update.wrapperHeight; display.lastWrapWidth = update.wrapperWidth; startWorker(cm, 400); } display.updateLineNumbers = null; return true } function postUpdateDisplay(cm, update) { var viewport = update.viewport; for (var first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. if (viewport && viewport.top != null) { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } // Updated line heights might result in the drawn area not // actually covering the viewport. Keep looping until it does. update.visible = visibleLines(cm.display, cm.doc, viewport); if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) { break } } if (!updateDisplayIfNeeded(cm, update)) { break } updateHeightsInViewport(cm); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.force = false; } update.signal(cm, "update", cm); if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; } } function updateDisplaySimple(cm, viewport) { var update = new DisplayUpdate(cm, viewport); if (updateDisplayIfNeeded(cm, update)) { updateHeightsInViewport(cm); postUpdateDisplay(cm, update); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.finish(); } } // Sync the actual display DOM structure with display.view, removing // nodes for lines that are no longer in view, and creating the ones // that are not there yet, and updating the ones that are out of // date. function patchDisplay(cm, updateNumbersFrom, dims) { var display = cm.display, lineNumbers = cm.options.lineNumbers; var container = display.lineDiv, cur = container.firstChild; function rm(node) { var next = node.nextSibling; // Works around a throw-scroll bug in OS X Webkit if (webkit && mac && cm.display.currentWheelTarget == node) { node.style.display = "none"; } else { node.parentNode.removeChild(node); } return next } var view = display.view, lineN = display.viewFrom; // Loop over the elements in the view, syncing cur (the DOM nodes // in display.lineDiv) with the view as we go. for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet var node = buildLineElement(cm, lineView, lineN, dims); container.insertBefore(node, cur); } else { // Already drawn while (cur != lineView.node) { cur = rm(cur); } var updateNumber = lineNumbers && updateNumbersFrom != null && updateNumbersFrom <= lineN && lineView.lineNumber; if (lineView.changes) { if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } updateLineForChanges(cm, lineView, lineN, dims); } if (updateNumber) { removeChildren(lineView.lineNumber); lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); } cur = lineView.node.nextSibling; } lineN += lineView.size; } while (cur) { cur = rm(cur); } } function updateGutterSpace(cm) { var width = cm.display.gutters.offsetWidth; cm.display.sizer.style.marginLeft = width + "px"; } function setDocumentHeight(cm, measure) { cm.display.sizer.style.minHeight = measure.docHeight + "px"; cm.display.heightForcer.style.top = measure.docHeight + "px"; cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; } // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. function updateGutters(cm) { var gutters = cm.display.gutters, specs = cm.options.gutters; removeChildren(gutters); var i = 0; for (; i < specs.length; ++i) { var gutterClass = specs[i]; var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); if (gutterClass == "CodeMirror-linenumbers") { cm.display.lineGutter = gElt; gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; } } gutters.style.display = i ? "" : "none"; updateGutterSpace(cm); } // Make sure the gutters options contains the element // "CodeMirror-linenumbers" when the lineNumbers option is true. function setGuttersForLineNumbers(options) { var found = indexOf(options.gutters, "CodeMirror-linenumbers"); if (found == -1 && options.lineNumbers) { options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); } else if (found > -1 && !options.lineNumbers) { options.gutters = options.gutters.slice(0); options.gutters.splice(found, 1); } } // Since the delta values reported on mouse wheel events are // unstandardized between browsers and even browser versions, and // generally horribly unpredictable, this code starts by measuring // the scroll effect that the first few mouse wheel events have, // and, from that, detects the way it can convert deltas to pixel // offsets afterwards. // // The reason we want to know the amount a wheel event will scroll // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. var wheelSamples = 0, wheelPixelsPerUnit = null; // Fill in a browser-detected starting value on browsers where we // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel // scroll (if it is large enough). if (ie) { wheelPixelsPerUnit = -.53; } else if (gecko) { wheelPixelsPerUnit = 15; } else if (chrome) { wheelPixelsPerUnit = -.7; } else if (safari) { wheelPixelsPerUnit = -1/3; } function wheelEventDelta(e) { var dx = e.wheelDeltaX, dy = e.wheelDeltaY; if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } else if (dy == null) { dy = e.wheelDelta; } return {x: dx, y: dy} } function wheelEventPixels(e) { var delta = wheelEventDelta(e); delta.x *= wheelPixelsPerUnit; delta.y *= wheelPixelsPerUnit; return delta } function onScrollWheel(cm, e) { var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; var display = cm.display, scroll = display.scroller; // Quit if there's nothing to scroll here var canScrollX = scroll.scrollWidth > scroll.clientWidth; var canScrollY = scroll.scrollHeight > scroll.clientHeight; if (!(dx && canScrollX || dy && canScrollY)) { return } // Webkit browsers on OS X abort momentum scrolls when the target // of the scroll event is removed from the scrollable element. // This hack (see related code in patchDisplay) makes sure the // element is kept around. if (dy && mac && webkit) { outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { for (var i = 0; i < view.length; i++) { if (view[i].node == cur) { cm.display.currentWheelTarget = cur; break outer } } } } // On some browsers, horizontal scrolling will cause redraws to // happen before the gutter has been realigned, causing it to // wriggle around in a most unseemly way. When we have an // estimated pixels/delta value, we just handle horizontal // scrolling entirely here. It'll be slightly off from native, but // better than glitching out. if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { if (dy && canScrollY) { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); // Only prevent default scrolling if vertical scrolling is // actually possible. Otherwise, it causes vertical scroll // jitter on OSX trackpads when deltaX is small and deltaY // is large (issue #3579) if (!dy || (dy && canScrollY)) { e_preventDefault(e); } display.wheelStartX = null; // Abort measurement, if in progress return } // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). if (dy && wheelPixelsPerUnit != null) { var pixels = dy * wheelPixelsPerUnit; var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) { top = Math.max(0, top + pixels - 50); } else { bot = Math.min(cm.doc.height, bot + pixels + 50); } updateDisplaySimple(cm, {top: top, bottom: bot}); } if (wheelSamples < 20) { if (display.wheelStartX == null) { display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; display.wheelDX = dx; display.wheelDY = dy; setTimeout(function () { if (display.wheelStartX == null) { return } var movedX = scroll.scrollLeft - display.wheelStartX; var movedY = scroll.scrollTop - display.wheelStartY; var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || (movedX && display.wheelDX && movedX / display.wheelDX); display.wheelStartX = display.wheelStartY = null; if (!sample) { return } wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); ++wheelSamples; }, 200); } else { display.wheelDX += dx; display.wheelDY += dy; } } } // Selection objects are immutable. A new one is created every time // the selection changes. A selection is one or more non-overlapping // (and non-touching) ranges, sorted, and an integer that indicates // which one is the primary selection (the one that's scrolled into // view, that getCursor returns, etc). var Selection = function(ranges, primIndex) { this.ranges = ranges; this.primIndex = primIndex; }; Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; Selection.prototype.equals = function (other) { var this$1 = this; if (other == this) { return true } if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } for (var i = 0; i < this.ranges.length; i++) { var here = this$1.ranges[i], there = other.ranges[i]; if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } } return true }; Selection.prototype.deepCopy = function () { var this$1 = this; var out = []; for (var i = 0; i < this.ranges.length; i++) { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); } return new Selection(out, this.primIndex) }; Selection.prototype.somethingSelected = function () { var this$1 = this; for (var i = 0; i < this.ranges.length; i++) { if (!this$1.ranges[i].empty()) { return true } } return false }; Selection.prototype.contains = function (pos, end) { var this$1 = this; if (!end) { end = pos; } for (var i = 0; i < this.ranges.length; i++) { var range = this$1.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) { return i } } return -1 }; var Range = function(anchor, head) { this.anchor = anchor; this.head = head; }; Range.prototype.from = function () { return minPos(this.anchor, this.head) }; Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; // Take an unsorted, potentially overlapping set of ranges, and // build a selection out of it. 'Consumes' ranges array (modifying // it). function normalizeSelection(cm, ranges, primIndex) { var mayTouch = cm && cm.options.selectionsMayTouch; var prim = ranges[primIndex]; ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); primIndex = indexOf(ranges, prim); for (var i = 1; i < ranges.length; i++) { var cur = ranges[i], prev = ranges[i - 1]; var diff = cmp(prev.to(), cur.from()); if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; if (i <= primIndex) { --primIndex; } ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); } } return new Selection(ranges, primIndex) } function simpleSelection(anchor, head) { return new Selection([new Range(anchor, head || anchor)], 0) } // Compute the position of the end of a change (its 'to' property // refers to the pre-change end). function changeEnd(change) { if (!change.text) { return change.to } return Pos(change.from.line + change.text.length - 1, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) } // Adjust a position to refer to the post-change position of the // same text, or the end of the change if the change covers it. function adjustForChange(pos, change) { if (cmp(pos, change.from) < 0) { return pos } if (cmp(pos, change.to) <= 0) { return changeEnd(change) } var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } return Pos(line, ch) } function computeSelAfterChange(doc, change) { var out = []; for (var i = 0; i < doc.sel.ranges.length; i++) { var range = doc.sel.ranges[i]; out.push(new Range(adjustForChange(range.anchor, change), adjustForChange(range.head, change))); } return normalizeSelection(doc.cm, out, doc.sel.primIndex) } function offsetPos(pos, old, nw) { if (pos.line == old.line) { return Pos(nw.line, pos.ch - old.ch + nw.ch) } else { return Pos(nw.line + (pos.line - old.line), pos.ch) } } // Used by replaceSelections to allow moving the selection to the // start or around the replaced test. Hint may be "start" or "around". function computeReplacedSel(doc, changes, hint) { var out = []; var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; for (var i = 0; i < changes.length; i++) { var change = changes[i]; var from = offsetPos(change.from, oldPrev, newPrev); var to = offsetPos(changeEnd(change), oldPrev, newPrev); oldPrev = change.to; newPrev = to; if (hint == "around") { var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; out[i] = new Range(inv ? to : from, inv ? from : to); } else { out[i] = new Range(from, from); } } return new Selection(out, doc.sel.primIndex) } // Used to get the editor into a consistent state again when options change. function loadMode(cm) { cm.doc.mode = getMode(cm.options, cm.doc.modeOption); resetModeState(cm); } function resetModeState(cm) { cm.doc.iter(function (line) { if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } }); cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; startWorker(cm, 100); cm.state.modeGen++; if (cm.curOp) { regChange(cm); } } // DOCUMENT DATA STRUCTURE // By default, updates that start and end at the beginning of a line // are treated specially, in order to make the association of line // widgets and marker elements with the text behave more intuitive. function isWholeLineUpdate(doc, change) { return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && (!doc.cm || doc.cm.options.wholeLineUpdateBefore) } // Perform a change on the document data structure. function updateDoc(doc, change, markedSpans, estimateHeight$$1) { function spansFor(n) {return markedSpans ? markedSpans[n] : null} function update(line, text, spans) { updateLine(line, text, spans, estimateHeight$$1); signalLater(line, "change", line, change); } function linesFor(start, end) { var result = []; for (var i = start; i < end; ++i) { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); } return result } var from = change.from, to = change.to, text = change.text; var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; // Adjust the line structure if (change.full) { doc.insert(0, linesFor(0, text.length)); doc.remove(text.length, doc.size - text.length); } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = linesFor(0, text.length - 1); update(lastLine, lastLine.text, lastSpans); if (nlines) { doc.remove(from.line, nlines); } if (added.length) { doc.insert(from.line, added); } } else if (firstLine == lastLine) { if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { var added$1 = linesFor(1, text.length - 1); added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added$1); } } else if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); doc.remove(from.line + 1, nlines); } else { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); var added$2 = linesFor(1, text.length - 1); if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } doc.insert(from.line + 1, added$2); } signalLater(doc, "change", doc, change); } // Call f for all linked documents. function linkedDocs(doc, f, sharedHistOnly) { function propagate(doc, skip, sharedHist) { if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { var rel = doc.linked[i]; if (rel.doc == skip) { continue } var shared = sharedHist && rel.sharedHist; if (sharedHistOnly && !shared) { continue } f(rel.doc, shared); propagate(rel.doc, doc, shared); } } } propagate(doc, null, true); } // Attach a document to an editor. function attachDoc(cm, doc) { if (doc.cm) { throw new Error("This document is already in use.") } cm.doc = doc; doc.cm = cm; estimateLineHeights(cm); loadMode(cm); setDirectionClass(cm); if (!cm.options.lineWrapping) { findMaxLine(cm); } cm.options.mode = doc.modeOption; regChange(cm); } function setDirectionClass(cm) { (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); } function directionChanged(cm) { runInOp(cm, function () { setDirectionClass(cm); regChange(cm); }); } function History(startGen) { // Arrays of change events and selections. Doing something adds an // event to done and clears undo. Undoing moves events from done // to undone, redoing moves them in the other direction. this.done = []; this.undone = []; this.undoDepth = Infinity; // Used to track when changes can be merged into a single undo // event this.lastModTime = this.lastSelTime = 0; this.lastOp = this.lastSelOp = null; this.lastOrigin = this.lastSelOrigin = null; // Used by the isClean() method this.generation = this.maxGeneration = startGen || 1; } // Create a history change event from an updateDoc-style change // object. function historyChangeFromChange(doc, change) { var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); return histChange } // Pop all selection events off the end of a history array. Stop at // a change event. function clearSelectionEvents(array) { while (array.length) { var last = lst(array); if (last.ranges) { array.pop(); } else { break } } } // Find the top change event in the history. Pop off selection // events that are in the way. function lastChangeEvent(hist, force) { if (force) { clearSelectionEvents(hist.done); return lst(hist.done) } else if (hist.done.length && !lst(hist.done).ranges) { return lst(hist.done) } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { hist.done.pop(); return lst(hist.done) } } // Register a change in the history. Merges changes that are within // a single operation, or are close together with an origin that // allows merging (starting with "+") into a single event. function addChangeToHistory(doc, change, selAfter, opId) { var hist = doc.history; hist.undone.length = 0; var time = +new Date, cur; var last; if ((hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || change.origin.charAt(0) == "*")) && (cur = lastChangeEvent(hist, hist.lastOp == opId))) { // Merge this change into the last event last = lst(cur.changes); if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed last.to = changeEnd(change); } else { // Add new sub-event cur.changes.push(historyChangeFromChange(doc, change)); } } else { // Can not be merged, start a new event. var before = lst(hist.done); if (!before || !before.ranges) { pushSelectionToHistory(doc.sel, hist.done); } cur = {changes: [historyChangeFromChange(doc, change)], generation: hist.generation}; hist.done.push(cur); while (hist.done.length > hist.undoDepth) { hist.done.shift(); if (!hist.done[0].ranges) { hist.done.shift(); } } } hist.done.push(selAfter); hist.generation = ++hist.maxGeneration; hist.lastModTime = hist.lastSelTime = time; hist.lastOp = hist.lastSelOp = opId; hist.lastOrigin = hist.lastSelOrigin = change.origin; if (!last) { signal(doc, "historyAdded"); } } function selectionEventCanBeMerged(doc, origin, prev, sel) { var ch = origin.charAt(0); return ch == "*" || ch == "+" && prev.ranges.length == sel.ranges.length && prev.somethingSelected() == sel.somethingSelected() && new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) } // Called whenever the selection changes, sets the new selection as // the pending selection in the history, and pushes the old pending // selection into the 'done' array when it was significantly // different (in number of selected ranges, emptiness, or time). function addSelectionToHistory(doc, sel, opId, options) { var hist = doc.history, origin = options && options.origin; // A new event is started when the previous origin does not match // the current, or the origins don't allow matching. Origins // starting with * are always merged, those starting with + are // merged when similar and close together in time. if (opId == hist.lastSelOp || (origin && hist.lastSelOrigin == origin && (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) { hist.done[hist.done.length - 1] = sel; } else { pushSelectionToHistory(sel, hist.done); } hist.lastSelTime = +new Date; hist.lastSelOrigin = origin; hist.lastSelOp = opId; if (options && options.clearRedo !== false) { clearSelectionEvents(hist.undone); } } function pushSelectionToHistory(sel, dest) { var top = lst(dest); if (!(top && top.ranges && top.equals(sel))) { dest.push(sel); } } // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { var existing = change["spans_" + doc.id], n = 0; doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { if (line.markedSpans) { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } ++n; }); } // When un/re-doing restores text containing marked spans, those // that have been explicitly cleared should not be restored. function removeClearedSpans(spans) { if (!spans) { return null } var out; for (var i = 0; i < spans.length; ++i) { if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } else if (out) { out.push(spans[i]); } } return !out ? spans : out.length ? out : null } // Retrieve and filter the old marked spans stored in a change event. function getOldSpans(doc, change) { var found = change["spans_" + doc.id]; if (!found) { return null } var nw = []; for (var i = 0; i < change.text.length; ++i) { nw.push(removeClearedSpans(found[i])); } return nw } // Used for un/re-doing changes from the history. Combines the // result of computing the existing spans with the set of spans that // existed in the history (so that deleting around a span and then // undoing brings back the span). function mergeOldSpans(doc, change) { var old = getOldSpans(doc, change); var stretched = stretchSpansOverChange(doc, change); if (!old) { return stretched } if (!stretched) { return old } for (var i = 0; i < old.length; ++i) { var oldCur = old[i], stretchCur = stretched[i]; if (oldCur && stretchCur) { spans: for (var j = 0; j < stretchCur.length; ++j) { var span = stretchCur[j]; for (var k = 0; k < oldCur.length; ++k) { if (oldCur[k].marker == span.marker) { continue spans } } oldCur.push(span); } } else if (stretchCur) { old[i] = stretchCur; } } return old } // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two function copyHistoryArray(events, newGroup, instantiateSel) { var copy = []; for (var i = 0; i < events.length; ++i) { var event = events[i]; if (event.ranges) { copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); continue } var changes = event.changes, newChanges = []; copy.push({changes: newChanges}); for (var j = 0; j < changes.length; ++j) { var change = changes[j], m = (void 0); newChanges.push({from: change.from, to: change.to, text: change.text}); if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { if (indexOf(newGroup, Number(m[1])) > -1) { lst(newChanges)[prop] = change[prop]; delete change[prop]; } } } } } } return copy } // The 'scroll' parameter given to many of these indicated whether // the new cursor position should be scrolled into view after // modifying the selection. // If shift is held or the extend flag is set, extends a range to // include a given position (and optionally a second position). // Otherwise, simply returns the range between the given positions. // Used for cursor motion and such. function extendRange(range, head, other, extend) { if (extend) { var anchor = range.anchor; if (other) { var posBefore = cmp(head, anchor) < 0; if (posBefore != (cmp(other, anchor) < 0)) { anchor = head; head = other; } else if (posBefore != (cmp(head, other) < 0)) { head = other; } } return new Range(anchor, head) } else { return new Range(other || head, head) } } // Extend the primary selection range, discard the rest. function extendSelection(doc, head, other, options, extend) { if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); } // Extend all selections (pos is an array of selections with length // equal the number of selections) function extendSelections(doc, heads, options) { var out = []; var extend = doc.cm && (doc.cm.display.shift || doc.extend); for (var i = 0; i < doc.sel.ranges.length; i++) { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); setSelection(doc, newSel, options); } // Updates a single range in the selection. function replaceOneSelection(doc, i, range, options) { var ranges = doc.sel.ranges.slice(0); ranges[i] = range; setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); } // Reset the selection to a single range. function setSimpleSelection(doc, anchor, head, options) { setSelection(doc, simpleSelection(anchor, head), options); } // Give beforeSelectionChange handlers a change to influence a // selection update. function filterSelectionChange(doc, sel, options) { var obj = { ranges: sel.ranges, update: function(ranges) { var this$1 = this; this.ranges = []; for (var i = 0; i < ranges.length; i++) { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); } }, origin: options && options.origin }; signal(doc, "beforeSelectionChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } else { return sel } } function setSelectionReplaceHistory(doc, sel, options) { var done = doc.history.done, last = lst(done); if (last && last.ranges) { done[done.length - 1] = sel; setSelectionNoUndo(doc, sel, options); } else { setSelection(doc, sel, options); } } // Set a new selection. function setSelection(doc, sel, options) { setSelectionNoUndo(doc, sel, options); addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); } function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) { sel = filterSelectionChange(doc, sel, options); } var bias = options && options.bias || (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); if (!(options && options.scroll === false) && doc.cm) { ensureCursorVisible(doc.cm); } } function setSelectionInner(doc, sel) { if (sel.equals(doc.sel)) { return } doc.sel = sel; if (doc.cm) { doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; signalCursorActivity(doc.cm); } signalLater(doc, "cursorActivity", doc); } // Verify that the selection does not partially select any atomic // marked ranges. function reCheckSelection(doc) { setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); } // Return a selection that does not partially select any atomic // ranges. function skipAtomicInSelection(doc, sel, bias, mayClear) { var out; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); if (out || newAnchor != range.anchor || newHead != range.head) { if (!out) { out = sel.ranges.slice(0, i); } out[i] = new Range(newAnchor, newHead); } } return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel } function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { var line = getLine(doc, pos.line); if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var sp = line.markedSpans[i], m = sp.marker; if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { signal(m, "beforeCursorEnter"); if (m.explicitlyCleared) { if (!line.markedSpans) { break } else {--i; continue} } } if (!m.atomic) { continue } if (oldPos) { var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) { return skipAtomicInner(doc, near, pos, dir, mayClear) } } var far = m.find(dir < 0 ? -1 : 1); if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null } } } return pos } // Ensure a given position is not inside an atomic range. function skipAtomic(doc, pos, oldPos, bias, mayClear) { var dir = bias || 1; var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); if (!found) { doc.cantEdit = true; return Pos(doc.first, 0) } return found } function movePos(doc, pos, dir, line) { if (dir < 0 && pos.ch == 0) { if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } else { return null } } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } else { return null } } else { return new Pos(pos.line, pos.ch + dir) } } function selectAll(cm) { cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); } // UPDATING // Allow "beforeChange" event handlers to influence a change function filterChange(doc, change, update) { var obj = { canceled: false, from: change.from, to: change.to, text: change.text, origin: change.origin, cancel: function () { return obj.canceled = true; } }; if (update) { obj.update = function (from, to, text, origin) { if (from) { obj.from = clipPos(doc, from); } if (to) { obj.to = clipPos(doc, to); } if (text) { obj.text = text; } if (origin !== undefined) { obj.origin = origin; } }; } signal(doc, "beforeChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } if (obj.canceled) { return null } return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} } // Apply a change to a document, and add it to the document's // history, and propagating it to all linked documents. function makeChange(doc, change, ignoreReadOnly) { if (doc.cm) { if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } if (doc.cm.state.suppressEdits) { return } } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { change = filterChange(doc, change, true); if (!change) { return } } // Possibly split or suppress the update based on the presence // of read-only spans in its range. var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); if (split) { for (var i = split.length - 1; i >= 0; --i) { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } } else { makeChangeInner(doc, change); } } function makeChangeInner(doc, change) { if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } var selAfter = computeSelAfterChange(doc, change); addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); var rebased = []; linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); }); } // Revert a change stored in a document's history. function makeChangeFromHistory(doc, type, allowSelectionOnly) { var suppress = doc.cm && doc.cm.state.suppressEdits; if (suppress && !allowSelectionOnly) { return } var hist = doc.history, event, selAfter = doc.sel; var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; // Verify that there is a useable event (so that ctrl-z won't // needlessly clear selection events) var i = 0; for (; i < source.length; i++) { event = source[i]; if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) { break } } if (i == source.length) { return } hist.lastOrigin = hist.lastSelOrigin = null; for (;;) { event = source.pop(); if (event.ranges) { pushSelectionToHistory(event, dest); if (allowSelectionOnly && !event.equals(doc.sel)) { setSelection(doc, event, {clearRedo: false}); return } selAfter = event; } else if (suppress) { source.push(event); return } else { break } } // Build up a reverse change object to add to the opposite history // stack (redo when undoing, and vice versa). var antiChanges = []; pushSelectionToHistory(selAfter, dest); dest.push({changes: antiChanges, generation: hist.generation}); hist.generation = event.generation || ++hist.maxGeneration; var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); var loop = function ( i ) { var change = event.changes[i]; change.origin = type; if (filter && !filterChange(doc, change, false)) { source.length = 0; return {} } antiChanges.push(historyChangeFromChange(doc, change)); var after = i ? computeSelAfterChange(doc, change) : lst(source); makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } var rebased = []; // Propagate to the linked documents linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); }); }; for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { var returned = loop( i$1 ); if ( returned ) return returned.v; } } // Sub-views need their line numbers shifted when text is added // above or below them in the parent document. function shiftDoc(doc, distance) { if (distance == 0) { return } doc.first += distance; doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( Pos(range.anchor.line + distance, range.anchor.ch), Pos(range.head.line + distance, range.head.ch) ); }), doc.sel.primIndex); if (doc.cm) { regChange(doc.cm, doc.first, doc.first - distance, distance); for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) { regLineChange(doc.cm, l, "gutter"); } } } // More lower-level change function, handling only a single document // (not linked ones). function makeChangeSingleDoc(doc, change, selAfter, spans) { if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } if (change.to.line < doc.first) { shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); return } if (change.from.line > doc.lastLine()) { return } // Clip the change to the size of this doc if (change.from.line < doc.first) { var shift = change.text.length - 1 - (doc.first - change.from.line); shiftDoc(doc, shift); change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), text: [lst(change.text)], origin: change.origin}; } var last = doc.lastLine(); if (change.to.line > last) { change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), text: [change.text[0]], origin: change.origin}; } change.removed = getBetween(doc, change.from, change.to); if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } else { updateDoc(doc, change, spans); } setSelectionNoUndo(doc, selAfter, sel_dontScroll); } // Handle the interaction of a change to a document with the editor // that this document is part of. function makeChangeSingleDocInEditor(cm, change, spans) { var doc = cm.doc, display = cm.display, from = change.from, to = change.to; var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); doc.iter(checkWidthStart, to.line + 1, function (line) { if (line == display.maxLine) { recomputeMaxLength = true; return true } }); } if (doc.sel.contains(change.from, change.to) > -1) { signalCursorActivity(cm); } updateDoc(doc, change, spans, estimateHeight(cm)); if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function (line) { var len = lineLength(line); if (len > display.maxLineLength) { display.maxLine = line; display.maxLineLength = len; display.maxLineChanged = true; recomputeMaxLength = false; } }); if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } } retreatFrontier(doc, from.line); startWorker(cm, 400); var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display if (change.full) { regChange(cm); } else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) { regLineChange(cm, from.line, "text"); } else { regChange(cm, from.line, to.line + 1, lendiff); } var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); if (changeHandler || changesHandler) { var obj = { from: from, to: to, text: change.text, removed: change.removed, origin: change.origin }; if (changeHandler) { signalLater(cm, "change", cm, obj); } if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } } cm.display.selForContextMenu = null; } function replaceRange(doc, code, from, to, origin) { var assign; if (!to) { to = from; } if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } if (typeof code == "string") { code = doc.splitLines(code); } makeChange(doc, {from: from, to: to, text: code, origin: origin}); } // Rebasing/resetting history to deal with externally-sourced changes function rebaseHistSelSingle(pos, from, to, diff) { if (to < pos.line) { pos.line += diff; } else if (from < pos.line) { pos.line = from; pos.ch = 0; } } // Tries to rebase an array of history events given a change in the // document. If the change touches the same lines as the event, the // event, and everything 'behind' it, is discarded. If the change is // before the event, the event's positions are updated. Uses a // copy-on-write scheme for the positions, to avoid having to // reallocate them all on every rebase, but also avoid problems with // shared position objects being unsafely updated. function rebaseHistArray(array, from, to, diff) { for (var i = 0; i < array.length; ++i) { var sub = array[i], ok = true; if (sub.ranges) { if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } for (var j = 0; j < sub.ranges.length; j++) { rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); } continue } for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { var cur = sub.changes[j$1]; if (to < cur.from.line) { cur.from = Pos(cur.from.line + diff, cur.from.ch); cur.to = Pos(cur.to.line + diff, cur.to.ch); } else if (from <= cur.to.line) { ok = false; break } } if (!ok) { array.splice(0, i + 1); i = 0; } } } function rebaseHist(hist, change) { var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; rebaseHistArray(hist.done, from, to, diff); rebaseHistArray(hist.undone, from, to, diff); } // Utility for applying a change to a line by handle or number, // returning the number and optionally registering the line as // changed. function changeLine(doc, handle, changeType, op) { var no = handle, line = handle; if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } else { no = lineNo(handle); } if (no == null) { return null } if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } return line } // The document is represented as a BTree consisting of leaves, with // chunk of lines in them, and branches, with up to ten leaves or // other branch nodes below them. The top node is always a branch // node, and is the document object itself (meaning it has // additional methods and properties). // // All nodes have parent links. The tree is used both to go from // line numbers to line objects, and to go from objects to numbers. // It also indexes by height, and is used to convert between height // and line object, and to find the total height of the document. // // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html function LeafChunk(lines) { var this$1 = this; this.lines = lines; this.parent = null; var height = 0; for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; height += lines[i].height; } this.height = height; } LeafChunk.prototype = { chunkSize: function() { return this.lines.length }, // Remove the n lines at offset 'at'. removeInner: function(at, n) { var this$1 = this; for (var i = at, e = at + n; i < e; ++i) { var line = this$1.lines[i]; this$1.height -= line.height; cleanUpLine(line); signalLater(line, "delete"); } this.lines.splice(at, n); }, // Helper used to collapse a small branch into a single leaf. collapse: function(lines) { lines.push.apply(lines, this.lines); }, // Insert the given array of lines at offset 'at', count them as // having the given height. insertInner: function(at, lines, height) { var this$1 = this; this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; } }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { var this$1 = this; for (var e = at + n; at < e; ++at) { if (op(this$1.lines[at])) { return true } } } }; function BranchChunk(children) { var this$1 = this; this.children = children; var size = 0, height = 0; for (var i = 0; i < children.length; ++i) { var ch = children[i]; size += ch.chunkSize(); height += ch.height; ch.parent = this$1; } this.size = size; this.height = height; this.parent = null; } BranchChunk.prototype = { chunkSize: function() { return this.size }, removeInner: function(at, n) { var this$1 = this; this.size -= n; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; child.removeInner(at, rm); this$1.height -= oldHeight - child.height; if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) { break } at = 0; } else { at -= sz; } } // If the result is smaller than 25 lines, ensure that it is a // single leaf node. if (this.size - n < 25 && (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { var lines = []; this.collapse(lines); this.children = [new LeafChunk(lines)]; this.children[0].parent = this; } }, collapse: function(lines) { var this$1 = this; for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); } }, insertInner: function(at, lines, height) { var this$1 = this; this.size += lines.length; this.height += height; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. var remaining = child.lines.length % 25 + 25; for (var pos = remaining; pos < child.lines.length;) { var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; this$1.children.splice(++i, 0, leaf); leaf.parent = this$1; } child.lines = child.lines.slice(0, remaining); this$1.maybeSpill(); } break } at -= sz; } }, // When a node has grown, check whether it should be split. maybeSpill: function() { if (this.children.length <= 10) { return } var me = this; do { var spilled = me.children.splice(me.children.length - 5, 5); var sibling = new BranchChunk(spilled); if (!me.parent) { // Become the parent node var copy = new BranchChunk(me.children); copy.parent = me; me.children = [copy, sibling]; me = copy; } else { me.size -= sibling.size; me.height -= sibling.height; var myIndex = indexOf(me.parent.children, me); me.parent.children.splice(myIndex + 1, 0, sibling); } sibling.parent = me.parent; } while (me.children.length > 10) me.parent.maybeSpill(); }, iterN: function(at, n, op) { var this$1 = this; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at < sz) { var used = Math.min(n, sz - at); if (child.iterN(at, used, op)) { return true } if ((n -= used) == 0) { break } at = 0; } else { at -= sz; } } } }; // Line widgets are block elements displayed above or below a line. var LineWidget = function(doc, node, options) { var this$1 = this; if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) { this$1[opt] = options[opt]; } } } this.doc = doc; this.node = node; }; LineWidget.prototype.clear = function () { var this$1 = this; var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); if (no == null || !ws) { return } for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } } if (!ws.length) { line.widgets = null; } var height = widgetHeight(this); updateLineHeight(line, Math.max(0, line.height - height)); if (cm) { runInOp(cm, function () { adjustScrollWhenAboveVisible(cm, line, -height); regLineChange(cm, no, "widget"); }); signalLater(cm, "lineWidgetCleared", cm, this, no); } }; LineWidget.prototype.changed = function () { var this$1 = this; var oldH = this.height, cm = this.doc.cm, line = this.line; this.height = null; var diff = widgetHeight(this) - oldH; if (!diff) { return } if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } if (cm) { runInOp(cm, function () { cm.curOp.forceUpdate = true; adjustScrollWhenAboveVisible(cm, line, diff); signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); }); } }; eventMixin(LineWidget); function adjustScrollWhenAboveVisible(cm, line, diff) { if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) { addToScrollTop(cm, diff); } } function addLineWidget(doc, handle, node, options) { var widget = new LineWidget(doc, node, options); var cm = doc.cm; if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } changeLine(doc, handle, "widget", function (line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) { widgets.push(widget); } else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } widget.line = line; if (cm && !lineIsHidden(doc, line)) { var aboveVisible = heightAtLine(line) < doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) { addToScrollTop(cm, widget.height); } cm.curOp.forceUpdate = true; } return true }); if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } return widget } // TEXTMARKERS // Created with markText and setBookmark methods. A TextMarker is a // handle that can be used to clear or find a marked position in the // document. Line objects hold arrays (markedSpans) containing // {from, to, marker} object pointing to such marker objects, and // indicating that such a marker is present on that line. Multiple // lines may point to the same marker when it spans across lines. // The spans will have null for their from/to properties when the // marker continues beyond the start/end of the line. Markers have // links back to the lines they currently touch. // Collapsed markers have unique ids, in order to be able to order // them, which is needed for uniquely determining an outer marker // when they overlap (they may nest, but not partially overlap). var nextMarkerId = 0; var TextMarker = function(doc, type) { this.lines = []; this.type = type; this.doc = doc; this.id = ++nextMarkerId; }; // Clear the marker. TextMarker.prototype.clear = function () { var this$1 = this; if (this.explicitlyCleared) { return } var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) { startOperation(cm); } if (hasHandler(this, "clear")) { var found = this.find(); if (found) { signalLater(this, "clear", found.from, found.to); } } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this$1.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this$1); if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); } else if (cm) { if (span.to != null) { max = lineNo(line); } if (span.from != null) { min = lineNo(line); } } line.markedSpans = removeMarkedSpan(line.markedSpans, span); if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) { updateLineHeight(line, textHeight(cm.display)); } } if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; cm.display.maxLineChanged = true; } } } if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } this.lines.length = 0; this.explicitlyCleared = true; if (this.atomic && this.doc.cantEdit) { this.doc.cantEdit = false; if (cm) { reCheckSelection(cm.doc); } } if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } if (withOp) { endOperation(cm); } if (this.parent) { this.parent.clear(); } }; // Find the position of the marker in the document. Returns a {from, // to} object by default. Side can be passed to get a specific side // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function (side, lineObj) { var this$1 = this; if (side == null && this.type == "bookmark") { side = 1; } var from, to; for (var i = 0; i < this.lines.length; ++i) { var line = this$1.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this$1); if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from); if (side == -1) { return from } } if (span.to != null) { to = Pos(lineObj ? line : lineNo(line), span.to); if (side == 1) { return to } } } return from && {from: from, to: to} }; // Signals that the marker's widget changed, and surrounding layout // should be recomputed. TextMarker.prototype.changed = function () { var this$1 = this; var pos = this.find(-1, true), widget = this, cm = this.doc.cm; if (!pos || !cm) { return } runInOp(cm, function () { var line = pos.line, lineN = lineNo(pos.line); var view = findViewForLine(cm, lineN); if (view) { clearLineMeasurementCacheFor(view); cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; } cm.curOp.updateMaxLine = true; if (!lineIsHidden(widget.doc, line) && widget.height != null) { var oldHeight = widget.height; widget.height = null; var dHeight = widgetHeight(widget) - oldHeight; if (dHeight) { updateLineHeight(line, line.height + dHeight); } } signalLater(cm, "markerChanged", cm, this$1); }); }; TextMarker.prototype.attachLine = function (line) { if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } } this.lines.push(line); }; TextMarker.prototype.detachLine = function (line) { this.lines.splice(indexOf(this.lines, line), 1); if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); } }; eventMixin(TextMarker); // Create a marker, wire it up to the right lines, and function markText(doc, from, to, options, type) { // Shared markers (across linked documents) are handled separately // (markTextShared will call out to this again, once per // document). if (options && options.shared) { return markTextShared(doc, from, to, options, type) } // Ensure we are in an operation. if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } var marker = new TextMarker(doc, type), diff = cmp(from, to); if (options) { copyObj(options, marker, false); } // Don't connect empty markers unless clearWhenEmpty is false if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) { return marker } if (marker.replacedWith) { // Showing up as a widget implies collapsed (widget replaces text) marker.collapsed = true; marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } if (options.insertLeft) { marker.widgetNode.insertLeft = true; } } if (marker.collapsed) { if (conflictingCollapsedRange(doc, from.line, from, to, marker) || from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) { throw new Error("Inserting collapsed marker partially overlapping an existing one") } seeCollapsedSpans(); } if (marker.addToHistory) { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function (line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) { updateMaxLine = true; } if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } addMarkedSpan(line, new MarkedSpan(marker, curLine == from.line ? from.ch : null, curLine == to.line ? to.ch : null)); ++curLine; }); // lineIsHidden depends on the presence of the spans, so needs a second pass if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } }); } if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } if (marker.readOnly) { seeReadOnlySpans(); if (doc.history.done.length || doc.history.undone.length) { doc.clearHistory(); } } if (marker.collapsed) { marker.id = ++nextMarkerId; marker.atomic = true; } if (cm) { // Sync editor state if (updateMaxLine) { cm.curOp.updateMaxLine = true; } if (marker.collapsed) { regChange(cm, from.line, to.line + 1); } else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } if (marker.atomic) { reCheckSelection(cm.doc); } signalLater(cm, "markerAdded", cm, marker); } return marker } // SHARED TEXTMARKERS // A shared marker spans multiple linked documents. It is // implemented as a meta-marker-object controlling multiple normal // markers. var SharedTextMarker = function(markers, primary) { var this$1 = this; this.markers = markers; this.primary = primary; for (var i = 0; i < markers.length; ++i) { markers[i].parent = this$1; } }; SharedTextMarker.prototype.clear = function () { var this$1 = this; if (this.explicitlyCleared) { return } this.explicitlyCleared = true; for (var i = 0; i < this.markers.length; ++i) { this$1.markers[i].clear(); } signalLater(this, "clear"); }; SharedTextMarker.prototype.find = function (side, lineObj) { return this.primary.find(side, lineObj) }; eventMixin(SharedTextMarker); function markTextShared(doc, from, to, options, type) { options = copyObj(options); options.shared = false; var markers = [markText(doc, from, to, options, type)], primary = markers[0]; var widget = options.widgetNode; linkedDocs(doc, function (doc) { if (widget) { options.widgetNode = widget.cloneNode(true); } markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); for (var i = 0; i < doc.linked.length; ++i) { if (doc.linked[i].isParent) { return } } primary = lst(markers); }); return new SharedTextMarker(markers, primary) } function findSharedMarkers(doc) { return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) } function copySharedMarkers(doc, markers) { for (var i = 0; i < markers.length; i++) { var marker = markers[i], pos = marker.find(); var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); if (cmp(mFrom, mTo)) { var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); marker.markers.push(subMark); subMark.parent = marker; } } } function detachSharedMarkers(markers) { var loop = function ( i ) { var marker = markers[i], linked = [marker.primary.doc]; linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); for (var j = 0; j < marker.markers.length; j++) { var subMarker = marker.markers[j]; if (indexOf(linked, subMarker.doc) == -1) { subMarker.parent = null; marker.markers.splice(j--, 1); } } }; for (var i = 0; i < markers.length; i++) loop( i ); } var nextDocId = 0; var Doc = function(text, mode, firstLine, lineSep, direction) { if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } if (firstLine == null) { firstLine = 0; } BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; this.cleanGeneration = 1; this.modeFrontier = this.highlightFrontier = firstLine; var start = Pos(firstLine, 0); this.sel = simpleSelection(start); this.history = new History(null); this.id = ++nextDocId; this.modeOption = mode; this.lineSep = lineSep; this.direction = (direction == "rtl") ? "rtl" : "ltr"; this.extend = false; if (typeof text == "string") { text = this.splitLines(text); } updateDoc(this, {from: start, to: start, text: text}); setSelection(this, simpleSelection(start), sel_dontScroll); }; Doc.prototype = createObj(BranchChunk.prototype, { constructor: Doc, // Iterate over the document. Supports two forms -- with only one // argument, it calls that for each line in the document. With // three, it iterates over the range given by the first two (with // the second being non-inclusive). iter: function(from, to, op) { if (op) { this.iterN(from - this.first, to - from, op); } else { this.iterN(this.first, this.first + this.size, from); } }, // Non-public interface for adding and removing lines. insert: function(at, lines) { var height = 0; for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } this.insertInner(at - this.first, lines, height); }, remove: function(at, n) { this.removeInner(at - this.first, n); }, // From here, the methods are part of the public interface. Most // are also available from CodeMirror (editor) instances. getValue: function(lineSep) { var lines = getLines(this, this.first, this.first + this.size); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, setValue: docMethodOp(function(code) { var top = Pos(this.first, 0), last = this.first + this.size - 1; makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true); if (this.cm) { scrollToCoords(this.cm, 0, 0); } setSelection(this, simpleSelection(top), sel_dontScroll); }), replaceRange: function(code, from, to, origin) { from = clipPos(this, from); to = to ? clipPos(this, to) : from; replaceRange(this, code, from, to, origin); }, getRange: function(from, to, lineSep) { var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, getLineNumber: function(line) {return lineNo(line)}, getLineHandleVisualStart: function(line) { if (typeof line == "number") { line = getLine(this, line); } return visualLine(line) }, lineCount: function() {return this.size}, firstLine: function() {return this.first}, lastLine: function() {return this.first + this.size - 1}, clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { var range$$1 = this.sel.primary(), pos; if (start == null || start == "head") { pos = range$$1.head; } else if (start == "anchor") { pos = range$$1.anchor; } else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); } else { pos = range$$1.from(); } return pos }, listSelections: function() { return this.sel.ranges }, somethingSelected: function() {return this.sel.somethingSelected()}, setCursor: docMethodOp(function(line, ch, options) { setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); }), setSelection: docMethodOp(function(anchor, head, options) { setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); }), extendSelection: docMethodOp(function(head, other, options) { extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); }), extendSelections: docMethodOp(function(heads, options) { extendSelections(this, clipPosArray(this, heads), options); }), extendSelectionsBy: docMethodOp(function(f, options) { var heads = map(this.sel.ranges, f); extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { var this$1 = this; if (!ranges.length) { return } var out = []; for (var i = 0; i < ranges.length; i++) { out[i] = new Range(clipPos(this$1, ranges[i].anchor), clipPos(this$1, ranges[i].head)); } if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } setSelection(this, normalizeSelection(this.cm, out, primary), options); }), addSelection: docMethodOp(function(anchor, head, options) { var ranges = this.sel.ranges.slice(0); ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); }), getSelection: function(lineSep) { var this$1 = this; var ranges = this.sel.ranges, lines; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); lines = lines ? lines.concat(sel) : sel; } if (lineSep === false) { return lines } else { return lines.join(lineSep || this.lineSeparator()) } }, getSelections: function(lineSep) { var this$1 = this; var parts = [], ranges = this.sel.ranges; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); } parts[i] = sel; } return parts }, replaceSelection: function(code, collapse, origin) { var dup = []; for (var i = 0; i < this.sel.ranges.length; i++) { dup[i] = code; } this.replaceSelections(dup, collapse, origin || "+input"); }, replaceSelections: docMethodOp(function(code, collapse, origin) { var this$1 = this; var changes = [], sel = this.sel; for (var i = 0; i < sel.ranges.length; i++) { var range$$1 = sel.ranges[i]; changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}; } var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) { makeChange(this$1, changes[i$1]); } if (newSel) { setSelectionReplaceHistory(this, newSel); } else if (this.cm) { ensureCursorVisible(this.cm); } }), undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), setExtending: function(val) {this.extend = val;}, getExtending: function() {return this.extend}, historySize: function() { var hist = this.history, done = 0, undone = 0; for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } return {undo: done, redo: undone} }, clearHistory: function() {this.history = new History(this.history.maxGeneration);}, markClean: function() { this.cleanGeneration = this.changeGeneration(true); }, changeGeneration: function(forceSplit) { if (forceSplit) { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } return this.history.generation }, isClean: function (gen) { return this.history.generation == (gen || this.cleanGeneration) }, getHistory: function() { return {done: copyHistoryArray(this.history.done), undone: copyHistoryArray(this.history.undone)} }, setHistory: function(histData) { var hist = this.history = new History(this.history.maxGeneration); hist.done = copyHistoryArray(histData.done.slice(0), null, true); hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); }, setGutterMarker: docMethodOp(function(line, gutterID, value) { return changeLine(this, line, "gutter", function (line) { var markers = line.gutterMarkers || (line.gutterMarkers = {}); markers[gutterID] = value; if (!value && isEmpty(markers)) { line.gutterMarkers = null; } return true }) }), clearGutter: docMethodOp(function(gutterID) { var this$1 = this; this.iter(function (line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { changeLine(this$1, line, "gutter", function () { line.gutterMarkers[gutterID] = null; if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } return true }); } }); }), lineInfo: function(line) { var n; if (typeof line == "number") { if (!isLine(this, line)) { return null } n = line; line = getLine(this, line); if (!line) { return null } } else { n = lineNo(line); if (n == null) { return null } } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, widgets: line.widgets} }, addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; if (!line[prop]) { line[prop] = cls; } else if (classTest(cls).test(line[prop])) { return false } else { line[prop] += " " + cls; } return true }) }), removeLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; var cur = line[prop]; if (!cur) { return false } else if (cls == null) { line[prop] = null; } else { var found = cur.match(classTest(cls)); if (!found) { return false } var end = found.index + found[0].length; line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; } return true }) }), addLineWidget: docMethodOp(function(handle, node, options) { return addLineWidget(this, handle, node, options) }), removeLineWidget: function(widget) { widget.clear(); }, markText: function(from, to, options) { return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") }, setBookmark: function(pos, options) { var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), insertLeft: options && options.insertLeft, clearWhenEmpty: false, shared: options && options.shared, handleMouseEvents: options && options.handleMouseEvents}; pos = clipPos(this, pos); return markText(this, pos, pos, realOpts, "bookmark") }, findMarksAt: function(pos) { pos = clipPos(this, pos); var markers = [], spans = getLine(this, pos.line).markedSpans; if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if ((span.from == null || span.from <= pos.ch) && (span.to == null || span.to >= pos.ch)) { markers.push(span.marker.parent || span.marker); } } } return markers }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to); var found = [], lineNo$$1 = from.line; this.iter(from.line, to.line + 1, function (line) { var spans = line.markedSpans; if (spans) { for (var i = 0; i < spans.length; i++) { var span = spans[i]; if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to || span.from == null && lineNo$$1 != from.line || span.from != null && lineNo$$1 == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) { found.push(span.marker.parent || span.marker); } } } ++lineNo$$1; }); return found }, getAllMarks: function() { var markers = []; this.iter(function (line) { var sps = line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { if (sps[i].from != null) { markers.push(sps[i].marker); } } } }); return markers }, posFromIndex: function(off) { var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length; this.iter(function (line) { var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true } off -= sz; ++lineNo$$1; }); return clipPos(this, Pos(lineNo$$1, ch)) }, indexFromPos: function (coords) { coords = clipPos(this, coords); var index = coords.ch; if (coords.line < this.first || coords.ch < 0) { return 0 } var sepSize = this.lineSeparator().length; this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value index += line.text.length + sepSize; }); return index }, copy: function(copyHistory) { var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first, this.lineSep, this.direction); doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; doc.sel = this.sel; doc.extend = false; if (copyHistory) { doc.history.undoDepth = this.history.undoDepth; doc.setHistory(this.getHistory()); } return doc }, linkedDoc: function(options) { if (!options) { options = {}; } var from = this.first, to = this.first + this.size; if (options.from != null && options.from > from) { from = options.from; } if (options.to != null && options.to < to) { to = options.to; } var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); if (options.sharedHist) { copy.history = this.history ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; copySharedMarkers(copy, findSharedMarkers(this)); return copy }, unlinkDoc: function(other) { var this$1 = this; if (other instanceof CodeMirror) { other = other.doc; } if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { var link = this$1.linked[i]; if (link.doc != other) { continue } this$1.linked.splice(i, 1); other.unlinkDoc(this$1); detachSharedMarkers(findSharedMarkers(this$1)); break } } // If the histories were shared, split them again if (other.history == this.history) { var splitIds = [other.id]; linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); other.history = new History(null); other.history.done = copyHistoryArray(this.history.done, splitIds); other.history.undone = copyHistoryArray(this.history.undone, splitIds); } }, iterLinkedDocs: function(f) {linkedDocs(this, f);}, getMode: function() {return this.mode}, getEditor: function() {return this.cm}, splitLines: function(str) { if (this.lineSep) { return str.split(this.lineSep) } return splitLinesAuto(str) }, lineSeparator: function() { return this.lineSep || "\n" }, setDirection: docMethodOp(function (dir) { if (dir != "rtl") { dir = "ltr"; } if (dir == this.direction) { return } this.direction = dir; this.iter(function (line) { return line.order = null; }); if (this.cm) { directionChanged(this.cm); } }) }); // Public alias. Doc.prototype.eachLine = Doc.prototype.iter; // Kludge to work around strange IE behavior where it'll sometimes // re-fire a series of drag-related events right after the drop (#1551) var lastDrop = 0; function onDrop(e) { var cm = this; clearDragCursor(cm); if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); if (ie) { lastDrop = +new Date; } var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; if (!pos || cm.isReadOnly()) { return } // Might be a file drop, in which case we simply extract the text // and insert it. if (files && files.length && window.FileReader && window.File) { var n = files.length, text = Array(n), read = 0; var loadFile = function (file, i) { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) { return } var reader = new FileReader; reader.onload = operation(cm, function () { var content = reader.result; if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; } text[i] = content; if (++read == n) { pos = clipPos(cm.doc, pos); var change = {from: pos, to: pos, text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), origin: "paste"}; makeChange(cm.doc, change); setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); } }); reader.readAsText(file); }; for (var i = 0; i < n; ++i) { loadFile(files[i], i); } } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(function () { return cm.display.input.focus(); }, 20); return } try { var text$1 = e.dataTransfer.getData("Text"); if (text$1) { var selected; if (cm.state.draggingText && !cm.state.draggingText.copy) { selected = cm.listSelections(); } setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } cm.replaceSelection(text$1, "around", "paste"); cm.display.input.focus(); } } catch(e){} } } function onDragStart(cm, e) { if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e.dataTransfer.setData("Text", cm.getSelection()); e.dataTransfer.effectAllowed = "copyMove"; // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; if (presto) { img.width = img.height = 1; cm.display.wrapper.appendChild(img); // Force a relayout, or Opera won't use our image for some obscure reason img._top = img.offsetTop; } e.dataTransfer.setDragImage(img, 0, 0); if (presto) { img.parentNode.removeChild(img); } } } function onDragOver(cm, e) { var pos = posFromMouse(cm, e); if (!pos) { return } var frag = document.createDocumentFragment(); drawSelectionCursor(cm, pos, frag); if (!cm.display.dragCursor) { cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); } removeChildrenAndAdd(cm.display.dragCursor, frag); } function clearDragCursor(cm) { if (cm.display.dragCursor) { cm.display.lineSpace.removeChild(cm.display.dragCursor); cm.display.dragCursor = null; } } // These must be handled carefully, because naively registering a // handler for each editor will cause the editors to never be // garbage collected. function forEachCodeMirror(f) { if (!document.getElementsByClassName) { return } var byClass = document.getElementsByClassName("CodeMirror"); for (var i = 0; i < byClass.length; i++) { var cm = byClass[i].CodeMirror; if (cm) { f(cm); } } } var globalsRegistered = false; function ensureGlobalHandlers() { if (globalsRegistered) { return } registerGlobalHandlers(); globalsRegistered = true; } function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. var resizeTimer; on(window, "resize", function () { if (resizeTimer == null) { resizeTimer = setTimeout(function () { resizeTimer = null; forEachCodeMirror(onResize); }, 100); } }); // When the window loses focus, we want to show the editor as blurred on(window, "blur", function () { return forEachCodeMirror(onBlur); }); } // Called when the window resizes function onResize(cm) { var d = cm.display; // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; d.scrollbarsClipped = false; cm.setSize(); } var keyNames = { 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" }; // Number keys for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } // Alphabetic keys for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } // Function keys for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } var keyMap = {}; keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", "Esc": "singleSelection" }; // Note that the save and find-related commands aren't defined by // default. User code or addons can define them. Unknown commands // are simply ignored. keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", "fallthrough": "basic" }; // Very basic readline/emacs-style bindings, which are standard on Mac. keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", "fallthrough": ["basic", "emacsy"] }; keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; // KEYMAP DISPATCH function normalizeKeyName(name) { var parts = name.split(/-(?!$)/); name = parts[parts.length - 1]; var alt, ctrl, shift, cmd; for (var i = 0; i < parts.length - 1; i++) { var mod = parts[i]; if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } else if (/^a(lt)?$/i.test(mod)) { alt = true; } else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } else if (/^s(hift)?$/i.test(mod)) { shift = true; } else { throw new Error("Unrecognized modifier name: " + mod) } } if (alt) { name = "Alt-" + name; } if (ctrl) { name = "Ctrl-" + name; } if (cmd) { name = "Cmd-" + name; } if (shift) { name = "Shift-" + name; } return name } // This is a kludge to keep keymaps mostly working as raw objects // (backwards compatibility) while at the same time support features // like normalization and multi-stroke key bindings. It compiles a // new normalized keymap, and then updates the old object to reflect // this. function normalizeKeyMap(keymap) { var copy = {}; for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { var value = keymap[keyname]; if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } if (value == "...") { delete keymap[keyname]; continue } var keys = map(keyname.split(" "), normalizeKeyName); for (var i = 0; i < keys.length; i++) { var val = (void 0), name = (void 0); if (i == keys.length - 1) { name = keys.join(" "); val = value; } else { name = keys.slice(0, i + 1).join(" "); val = "..."; } var prev = copy[name]; if (!prev) { copy[name] = val; } else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } } delete keymap[keyname]; } } for (var prop in copy) { keymap[prop] = copy[prop]; } return keymap } function lookupKey(key, map$$1, handle, context) { map$$1 = getKeyMap(map$$1); var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]; if (found === false) { return "nothing" } if (found === "...") { return "multi" } if (found != null && handle(found)) { return "handled" } if (map$$1.fallthrough) { if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]") { return lookupKey(key, map$$1.fallthrough, handle, context) } for (var i = 0; i < map$$1.fallthrough.length; i++) { var result = lookupKey(key, map$$1.fallthrough[i], handle, context); if (result) { return result } } } } // Modifier key presses don't count as 'real' key presses for the // purpose of keymap fallthrough. function isModifierKey(value) { var name = typeof value == "string" ? value : keyNames[value.keyCode]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" } function addModifierNames(name, event, noShift) { var base = name; if (event.altKey && base != "Alt") { name = "Alt-" + name; } if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } return name } // Look up the name of a key as indicated by an event object. function keyName(event, noShift) { if (presto && event.keyCode == 34 && event["char"]) { return false } var name = keyNames[event.keyCode]; if (name == null || event.altGraphKey) { return false } // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) if (event.keyCode == 3 && event.code) { name = event.code; } return addModifierNames(name, event, noShift) } function getKeyMap(val) { return typeof val == "string" ? keyMap[val] : val } // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. function deleteNearSelection(cm, compute) { var ranges = cm.doc.sel.ranges, kill = []; // Build up a set of ranges to kill first, merging overlapping // ranges. for (var i = 0; i < ranges.length; i++) { var toKill = compute(ranges[i]); while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { var replaced = kill.pop(); if (cmp(replaced.from, toKill.from) < 0) { toKill.from = replaced.from; break } } kill.push(toKill); } // Next, remove those actual ranges. runInOp(cm, function () { for (var i = kill.length - 1; i >= 0; i--) { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } ensureCursorVisible(cm); }); } function moveCharLogically(line, ch, dir) { var target = skipExtendingChars(line.text, ch + dir, dir); return target < 0 || target > line.text.length ? null : target } function moveLogically(line, start, dir) { var ch = moveCharLogically(line, start.ch, dir); return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") } function endOfLine(visually, cm, lineObj, lineNo, dir) { if (visually) { var order = getOrder(lineObj, cm.doc.direction); if (order) { var part = dir < 0 ? lst(order) : order[0]; var moveInStorageOrder = (dir < 0) == (part.level == 1); var sticky = moveInStorageOrder ? "after" : "before"; var ch; // With a wrapped rtl chunk (possibly spanning multiple bidi parts), // it could be that the last bidi part is not on the last visual line, // since visual lines contain content order-consecutive chunks. // Thus, in rtl, we are looking for the first (content-order) character // in the rtl chunk that is on the last line (that is, the same line // as the last (content-order) character). if (part.level > 0 || cm.doc.direction == "rtl") { var prep = prepareMeasureForLine(cm, lineObj); ch = dir < 0 ? lineObj.text.length - 1 : 0; var targetTop = measureCharPrepared(cm, prep, ch).top; ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } } else { ch = dir < 0 ? part.to : part.from; } return new Pos(lineNo, ch, sticky) } } return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") } function moveVisually(cm, line, start, dir) { var bidi = getOrder(line, cm.doc.direction); if (!bidi) { return moveLogically(line, start, dir) } if (start.ch >= line.text.length) { start.ch = line.text.length; start.sticky = "before"; } else if (start.ch <= 0) { start.ch = 0; start.sticky = "after"; } var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, // nothing interesting happens. return moveLogically(line, start, dir) } var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; var prep; var getWrappedLineExtent = function (ch) { if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } prep = prep || prepareMeasureForLine(cm, line); return wrappedLineExtentChar(cm, line, prep, ch) }; var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); if (cm.doc.direction == "rtl" || part.level == 1) { var moveInStorageOrder = (part.level == 1) == (dir < 0); var ch = mv(start, moveInStorageOrder ? 1 : -1); if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { // Case 2: We move within an rtl part or in an rtl editor on the same visual line var sticky = moveInStorageOrder ? "before" : "after"; return new Pos(start.line, ch, sticky) } } // Case 3: Could not move within this bidi part in this visual line, so leave // the current bidi part var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder ? new Pos(start.line, mv(ch, 1), "before") : new Pos(start.line, ch, "after"); }; for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { var part = bidi[partPos]; var moveInStorageOrder = (dir > 0) == (part.level != 1); var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } ch = moveInStorageOrder ? part.from : mv(part.to, -1); if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } } }; // Case 3a: Look for other bidi parts on the same visual line var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); if (res) { return res } // Case 3b: Look for other bidi parts on the next visual line var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); if (res) { return res } } // Case 4: Nowhere to move return null } // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. var commands = { selectAll: selectAll, singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, killLine: function (cm) { return deleteNearSelection(cm, function (range) { if (range.empty()) { var len = getLine(cm.doc, range.head.line).text.length; if (range.head.ch == len && range.head.line < cm.lastLine()) { return {from: range.head, to: Pos(range.head.line + 1, 0)} } else { return {from: range.head, to: Pos(range.head.line, len)} } } else { return {from: range.from(), to: range.to()} } }); }, deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) }); }); }, delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: range.from() }); }); }, delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var leftPos = cm.coordsChar({left: 0, top: top}, "div"); return {from: leftPos, to: range.from()} }); }, delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); return {from: range.from(), to: rightPos } }); }, undo: function (cm) { return cm.undo(); }, redo: function (cm) { return cm.redo(); }, undoSelection: function (cm) { return cm.undoSelection(); }, redoSelection: function (cm) { return cm.redoSelection(); }, goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, {origin: "+move", bias: 1} ); }, goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, {origin: "+move", bias: 1} ); }, goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, {origin: "+move", bias: -1} ); }, goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") }, sel_move); }, goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; return cm.coordsChar({left: 0, top: top}, "div") }, sel_move); }, goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; var pos = cm.coordsChar({left: 0, top: top}, "div"); if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } return pos }, sel_move); }, goLineUp: function (cm) { return cm.moveV(-1, "line"); }, goLineDown: function (cm) { return cm.moveV(1, "line"); }, goPageUp: function (cm) { return cm.moveV(-1, "page"); }, goPageDown: function (cm) { return cm.moveV(1, "page"); }, goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, goCharRight: function (cm) { return cm.moveH(1, "char"); }, goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, goColumnRight: function (cm) { return cm.moveH(1, "column"); }, goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, goGroupRight: function (cm) { return cm.moveH(1, "group"); }, goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, goWordRight: function (cm) { return cm.moveH(1, "word"); }, delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, indentAuto: function (cm) { return cm.indentSelection("smart"); }, indentMore: function (cm) { return cm.indentSelection("add"); }, indentLess: function (cm) { return cm.indentSelection("subtract"); }, insertTab: function (cm) { return cm.replaceSelection("\t"); }, insertSoftTab: function (cm) { var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].from(); var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); spaces.push(spaceStr(tabSize - col % tabSize)); } cm.replaceSelections(spaces); }, defaultTab: function (cm) { if (cm.somethingSelected()) { cm.indentSelection("add"); } else { cm.execCommand("insertTab"); } }, // Swap the two chars left and right of each selection's head. // Move cursor behind the two swapped characters afterwards. // // Doesn't consider line feeds a character. // Doesn't scan more than one line above to find a character. // Doesn't do anything on an empty line. // Doesn't do anything with non-empty selections. transposeChars: function (cm) { return runInOp(cm, function () { var ranges = cm.listSelections(), newSel = []; for (var i = 0; i < ranges.length; i++) { if (!ranges[i].empty()) { continue } var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; if (line) { if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } if (cur.ch > 0) { cur = new Pos(cur.line, cur.ch + 1); cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), Pos(cur.line, cur.ch - 2), cur, "+transpose"); } else if (cur.line > cm.doc.first) { var prev = getLine(cm.doc, cur.line - 1).text; if (prev) { cur = new Pos(cur.line, 1); cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); } } } newSel.push(new Range(cur, cur)); } cm.setSelections(newSel); }); }, newlineAndIndent: function (cm) { return runInOp(cm, function () { var sels = cm.listSelections(); for (var i = sels.length - 1; i >= 0; i--) { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } sels = cm.listSelections(); for (var i$1 = 0; i$1 < sels.length; i$1++) { cm.indentLine(sels[i$1].from().line, null, true); } ensureCursorVisible(cm); }); }, openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } }; function lineStart(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLine(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, visual, lineN, 1) } function lineEnd(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLineEnd(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, line, lineN, -1) } function lineStartSmart(cm, pos) { var start = lineStart(cm, pos.line); var line = getLine(cm.doc, start.line); var order = getOrder(line, cm.doc.direction); if (!order || order[0].level == 0) { var firstNonWS = Math.max(0, line.text.search(/\S/)); var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) } return start } // Run a handler that was bound to a key. function doHandleBinding(cm, bound, dropShift) { if (typeof bound == "string") { bound = commands[bound]; if (!bound) { return false } } // Ensure previous input has been read, so that the handler sees a // consistent view of the document cm.display.input.ensurePolled(); var prevShift = cm.display.shift, done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } if (dropShift) { cm.display.shift = false; } done = bound(cm) != Pass; } finally { cm.display.shift = prevShift; cm.state.suppressEdits = false; } return done } function lookupKeyForEditor(cm, name, handle) { for (var i = 0; i < cm.state.keyMaps.length; i++) { var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); if (result) { return result } } return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) || lookupKey(name, cm.options.keyMap, handle, cm) } // Note that, despite the name, this function is also used to check // for bound mouse clicks. var stopSeq = new Delayed; function dispatchKey(cm, name, e, handle) { var seq = cm.state.keySeq; if (seq) { if (isModifierKey(name)) { return "handled" } if (/\'$/.test(name)) { cm.state.keySeq = null; } else { stopSeq.set(50, function () { if (cm.state.keySeq == seq) { cm.state.keySeq = null; cm.display.input.reset(); } }); } if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } } return dispatchKeyInner(cm, name, e, handle) } function dispatchKeyInner(cm, name, e, handle) { var result = lookupKeyForEditor(cm, name, handle); if (result == "multi") { cm.state.keySeq = name; } if (result == "handled") { signalLater(cm, "keyHandled", cm, name, e); } if (result == "handled" || result == "multi") { e_preventDefault(e); restartBlink(cm); } return !!result } // Handle a key from the keydown event. function handleKeyBinding(cm, e) { var name = keyName(e, true); if (!name) { return false } if (e.shiftKey && !cm.state.keySeq) { // First try to resolve full name (including 'Shift-'). Failing // that, see if there is a cursor-motion command (starting with // 'go') bound to the keyname without 'Shift-'. return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) || dispatchKey(cm, name, e, function (b) { if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) { return doHandleBinding(cm, b) } }) } else { return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) } } // Handle a key from the keypress event function handleCharBinding(cm, e, ch) { return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) } var lastStoppedKey = null; function onKeyDown(e) { var cm = this; cm.curOp.focus = activeElt(); if (signalDOMEvent(cm, e)) { return } // IE does strange things with escape. if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } var code = e.keyCode; cm.display.shift = code == 16 || e.shiftKey; var handled = handleKeyBinding(cm, e); if (presto) { lastStoppedKey = handled ? code : null; // Opera has no cut event... we try to at least catch the key combo if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) { cm.replaceSelection("", null, "cut"); } } // Turn mouse into crosshair when Alt is held on Mac. if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) { showCrossHair(cm); } } function showCrossHair(cm) { var lineDiv = cm.display.lineDiv; addClass(lineDiv, "CodeMirror-crosshair"); function up(e) { if (e.keyCode == 18 || !e.altKey) { rmClass(lineDiv, "CodeMirror-crosshair"); off(document, "keyup", up); off(document, "mouseover", up); } } on(document, "keyup", up); on(document, "mouseover", up); } function onKeyUp(e) { if (e.keyCode == 16) { this.doc.sel.shift = false; } signalDOMEvent(this, e); } function onKeyPress(e) { var cm = this; if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } var ch = String.fromCharCode(charCode == null ? keyCode : charCode); // Some browsers fire keypress events for backspace if (ch == "\x08") { return } if (handleCharBinding(cm, e, ch)) { return } cm.display.input.onKeyPress(e); } var DOUBLECLICK_DELAY = 400; var PastClick = function(time, pos, button) { this.time = time; this.pos = pos; this.button = button; }; PastClick.prototype.compare = function (time, pos, button) { return this.time + DOUBLECLICK_DELAY > time && cmp(pos, this.pos) == 0 && button == this.button }; var lastClick, lastDoubleClick; function clickRepeat(pos, button) { var now = +new Date; if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { lastClick = lastDoubleClick = null; return "triple" } else if (lastClick && lastClick.compare(now, pos, button)) { lastDoubleClick = new PastClick(now, pos, button); lastClick = null; return "double" } else { lastClick = new PastClick(now, pos, button); lastDoubleClick = null; return "single" } } // A mouse down can be a single click, double click, triple click, // start of selection drag, start of text drag, new cursor // (ctrl-click), rectangle drag (alt-drag), or xwin // middle-click-paste. Or it might be a click on something we should // not interfere with, such as a scrollbar or widget. function onMouseDown(e) { var cm = this, display = cm.display; if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } display.input.ensurePolled(); display.shift = e.shiftKey; if (eventInWidget(display, e)) { if (!webkit) { // Briefly turn off draggability, to allow widgets to do // normal dragging things. display.scroller.draggable = false; setTimeout(function () { return display.scroller.draggable = true; }, 100); } return } if (clickInGutter(cm, e)) { return } var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; window.focus(); // #3261: make sure, that we're not starting a second selection if (button == 1 && cm.state.selectingText) { cm.state.selectingText(e); } if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } if (button == 1) { if (pos) { leftButtonDown(cm, pos, repeat, e); } else if (e_target(e) == display.scroller) { e_preventDefault(e); } } else if (button == 2) { if (pos) { extendSelection(cm.doc, pos); } setTimeout(function () { return display.input.focus(); }, 20); } else if (button == 3) { if (captureRightClick) { cm.display.input.onContextMenu(e); } else { delayBlurEvent(cm); } } } function handleMappedButton(cm, button, pos, repeat, event) { var name = "Click"; if (repeat == "double") { name = "Double" + name; } else if (repeat == "triple") { name = "Triple" + name; } name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { if (typeof bound == "string") { bound = commands[bound]; } if (!bound) { return false } var done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } done = bound(cm, pos) != Pass; } finally { cm.state.suppressEdits = false; } return done }) } function configureMouse(cm, repeat, event) { var option = cm.getOption("configureMouse"); var value = option ? option(cm, repeat, event) : {}; if (value.unit == null) { var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; } if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } return value } function leftButtonDown(cm, pos, repeat, event) { if (ie) { setTimeout(bind(ensureFocus, cm), 0); } else { cm.curOp.focus = activeElt(); } var behavior = configureMouse(cm, repeat, event); var sel = cm.doc.sel, contained; if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && repeat == "single" && (contained = sel.contains(pos)) > -1 && (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) { leftButtonStartDrag(cm, event, pos, behavior); } else { leftButtonSelect(cm, event, pos, behavior); } } // Start a text drag. When it ends, see if any dragging actually // happen, and treat as a click if it didn't. function leftButtonStartDrag(cm, event, pos, behavior) { var display = cm.display, moved = false; var dragEnd = operation(cm, function (e) { if (webkit) { display.scroller.draggable = false; } cm.state.draggingText = false; off(display.wrapper.ownerDocument, "mouseup", dragEnd); off(display.wrapper.ownerDocument, "mousemove", mouseMove); off(display.scroller, "dragstart", dragStart); off(display.scroller, "drop", dragEnd); if (!moved) { e_preventDefault(e); if (!behavior.addNew) { extendSelection(cm.doc, pos, null, null, behavior.extend); } // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) if (webkit || ie && ie_version == 9) { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); } else { display.input.focus(); } } }); var mouseMove = function(e2) { moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; }; var dragStart = function () { return moved = true; }; // Let the drag handler handle this. if (webkit) { display.scroller.draggable = true; } cm.state.draggingText = dragEnd; dragEnd.copy = !behavior.moveOnDrag; // IE's approach to draggable if (display.scroller.dragDrop) { display.scroller.dragDrop(); } on(display.wrapper.ownerDocument, "mouseup", dragEnd); on(display.wrapper.ownerDocument, "mousemove", mouseMove); on(display.scroller, "dragstart", dragStart); on(display.scroller, "drop", dragEnd); delayBlurEvent(cm); setTimeout(function () { return display.input.focus(); }, 20); } function rangeForUnit(cm, pos, unit) { if (unit == "char") { return new Range(pos, pos) } if (unit == "word") { return cm.findWordAt(pos) } if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } var result = unit(cm, pos); return new Range(result.from, result.to) } // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, event, start, behavior) { var display = cm.display, doc = cm.doc; e_preventDefault(event); var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; if (behavior.addNew && !behavior.extend) { ourIndex = doc.sel.contains(start); if (ourIndex > -1) { ourRange = ranges[ourIndex]; } else { ourRange = new Range(start, start); } } else { ourRange = doc.sel.primary(); ourIndex = doc.sel.primIndex; } if (behavior.unit == "rectangle") { if (!behavior.addNew) { ourRange = new Range(start, start); } start = posFromMouse(cm, event, true, true); ourIndex = -1; } else { var range$$1 = rangeForUnit(cm, start, behavior.unit); if (behavior.extend) { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); } else { ourRange = range$$1; } } if (!behavior.addNew) { ourIndex = 0; setSelection(doc, new Selection([ourRange], 0), sel_mouse); startSel = doc.sel; } else if (ourIndex == -1) { ourIndex = ranges.length; setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), {scroll: false, origin: "*mouse"}); } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), {scroll: false, origin: "*mouse"}); startSel = doc.sel; } else { replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); } var lastPos = start; function extendTo(pos) { if (cmp(lastPos, pos) == 0) { return } lastPos = pos; if (behavior.unit == "rectangle") { var ranges = [], tabSize = cm.options.tabSize; var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); line <= end; line++) { var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); if (left == right) { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } else if (text.length > leftPos) { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } } if (!ranges.length) { ranges.push(new Range(start, start)); } setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), {origin: "*mouse", scroll: false}); cm.scrollIntoView(pos); } else { var oldRange = ourRange; var range$$1 = rangeForUnit(cm, pos, behavior.unit); var anchor = oldRange.anchor, head; if (cmp(range$$1.anchor, anchor) > 0) { head = range$$1.head; anchor = minPos(oldRange.from(), range$$1.anchor); } else { head = range$$1.anchor; anchor = maxPos(oldRange.to(), range$$1.head); } var ranges$1 = startSel.ranges.slice(0); ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); } } var editorSize = display.wrapper.getBoundingClientRect(); // Used to ensure timeout re-tries don't fire when another extend // happened in the meantime (clearTimeout isn't reliable -- at // least on Chrome, the timeouts still happen even when cleared, // if the clear happens after their scheduled firing time). var counter = 0; function extend(e) { var curCount = ++counter; var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); if (!cur) { return } if (cmp(cur, lastPos) != 0) { cm.curOp.focus = activeElt(); extendTo(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } } else { var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; if (outside) { setTimeout(operation(cm, function () { if (counter != curCount) { return } display.scroller.scrollTop += outside; extend(e); }), 50); } } } function done(e) { cm.state.selectingText = false; counter = Infinity; e_preventDefault(e); display.input.focus(); off(display.wrapper.ownerDocument, "mousemove", move); off(display.wrapper.ownerDocument, "mouseup", up); doc.history.lastSelOrigin = null; } var move = operation(cm, function (e) { if (e.buttons === 0 || !e_button(e)) { done(e); } else { extend(e); } }); var up = operation(cm, done); cm.state.selectingText = up; on(display.wrapper.ownerDocument, "mousemove", move); on(display.wrapper.ownerDocument, "mouseup", up); } // Used when mouse-selecting to adjust the anchor to the proper side // of a bidi jump depending on the visual position of the head. function bidiSimplify(cm, range$$1) { var anchor = range$$1.anchor; var head = range$$1.head; var anchorLine = getLine(cm.doc, anchor.line); if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 } var order = getOrder(anchorLine); if (!order) { return range$$1 } var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 } var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); if (boundary == 0 || boundary == order.length) { return range$$1 } // Compute the relative visual position of the head compared to the // anchor (<0 is to the left, >0 to the right) var leftSide; if (head.line != anchor.line) { leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; } else { var headIndex = getBidiPartAt(order, head.ch, head.sticky); var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); if (headIndex == boundary - 1 || headIndex == boundary) { leftSide = dir < 0; } else { leftSide = dir > 0; } } var usePart = order[boundary + (leftSide ? -1 : 0)]; var from = leftSide == (usePart.level == 1); var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head) } // Determines whether an event happened in the gutter, and fires the // handlers for the corresponding event. function gutterEvent(cm, e, type, prevent) { var mX, mY; if (e.touches) { mX = e.touches[0].clientX; mY = e.touches[0].clientY; } else { try { mX = e.clientX; mY = e.clientY; } catch(e) { return false } } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } if (prevent) { e_preventDefault(e); } var display = cm.display; var lineBox = display.lineDiv.getBoundingClientRect(); if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } mY -= lineBox.top - display.viewOffset; for (var i = 0; i < cm.options.gutters.length; ++i) { var g = display.gutters.childNodes[i]; if (g && g.getBoundingClientRect().right >= mX) { var line = lineAtHeight(cm.doc, mY); var gutter = cm.options.gutters[i]; signal(cm, type, cm, line, gutter, e); return e_defaultPrevented(e) } } } function clickInGutter(cm, e) { return gutterEvent(cm, e, "gutterClick", true) } // CONTEXT MENU HANDLING // To make the context menu work, we need to briefly unhide the // textarea (making it as unobtrusive as possible) to let the // right-click take effect on it. function onContextMenu(cm, e) { if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } if (signalDOMEvent(cm, e, "contextmenu")) { return } if (!captureRightClick) { cm.display.input.onContextMenu(e); } } function contextMenuInGutter(cm, e) { if (!hasHandler(cm, "gutterContextMenu")) { return false } return gutterEvent(cm, e, "gutterContextMenu", false) } function themeChanged(cm) { cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); clearCaches(cm); } var Init = {toString: function(){return "CodeMirror.Init"}}; var defaults = {}; var optionHandlers = {}; function defineOptions(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; function option(name, deflt, handle, notOnInit) { CodeMirror.defaults[name] = deflt; if (handle) { optionHandlers[name] = notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } } CodeMirror.defineOption = option; // Passed to option handlers when there is no old value. CodeMirror.Init = Init; // These two are, on init, called from the constructor because they // have to be initialized before the editor can start at all. option("value", "", function (cm, val) { return cm.setValue(val); }, true); option("mode", null, function (cm, val) { cm.doc.modeOption = val; loadMode(cm); }, true); option("indentUnit", 2, loadMode, true); option("indentWithTabs", false); option("smartIndent", true); option("tabSize", 4, function (cm) { resetModeState(cm); clearCaches(cm); regChange(cm); }, true); option("lineSeparator", null, function (cm, val) { cm.doc.lineSep = val; if (!val) { return } var newBreaks = [], lineNo = cm.doc.first; cm.doc.iter(function (line) { for (var pos = 0;;) { var found = line.text.indexOf(val, pos); if (found == -1) { break } pos = found + val.length; newBreaks.push(Pos(lineNo, found)); } lineNo++; }); for (var i = newBreaks.length - 1; i >= 0; i--) { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } }); option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != Init) { cm.refresh(); } }); option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); option("electricChars", true); option("inputStyle", mobile ? "contenteditable" : "textarea", function () { throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME }, true); option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); option("rtlMoveVisually", !windows); option("wholeLineUpdateBefore", true); option("theme", "default", function (cm) { themeChanged(cm); guttersChanged(cm); }, true); option("keyMap", "default", function (cm, val, old) { var next = getKeyMap(val); var prev = old != Init && getKeyMap(old); if (prev && prev.detach) { prev.detach(cm, next); } if (next.attach) { next.attach(cm, prev || null); } }); option("extraKeys", null); option("configureMouse", null); option("lineWrapping", false, wrappingChanged, true); option("gutters", [], function (cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); }, true); option("fixedGutter", true, function (cm, val) { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; cm.refresh(); }, true); option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); option("scrollbarStyle", "native", function (cm) { initScrollbars(cm); updateScrollbars(cm); cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); }, true); option("lineNumbers", false, function (cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); }, true); option("firstLineNumber", 1, guttersChanged, true); option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true); option("showCursorWhenSelecting", false, updateSelection, true); option("resetSelectionOnContextMenu", true); option("lineWiseCopyCut", true); option("pasteLinesPerSelection", true); option("selectionsMayTouch", false); option("readOnly", false, function (cm, val) { if (val == "nocursor") { onBlur(cm); cm.display.input.blur(); } cm.display.input.readOnlyChanged(val); }); option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); option("dragDrop", true, dragDropChanged); option("allowDropFileTypes", null); option("cursorBlinkRate", 530); option("cursorScrollMargin", 0); option("cursorHeight", 1, updateSelection, true); option("singleCursorHeightPerLine", true, updateSelection, true); option("workTime", 100); option("workDelay", 100); option("flattenSpans", true, resetModeState, true); option("addModeClass", false, resetModeState, true); option("pollInterval", 100); option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); option("historyEventDelay", 1250); option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); option("maxHighlightLength", 10000, resetModeState, true); option("moveInputWithCursor", true, function (cm, val) { if (!val) { cm.display.input.resetPosition(); } }); option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); option("autofocus", null); option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); option("phrases", null); } function guttersChanged(cm) { updateGutters(cm); regChange(cm); alignHorizontally(cm); } function dragDropChanged(cm, value, old) { var wasOn = old && old != Init; if (!value != !wasOn) { var funcs = cm.display.dragFunctions; var toggle = value ? on : off; toggle(cm.display.scroller, "dragstart", funcs.start); toggle(cm.display.scroller, "dragenter", funcs.enter); toggle(cm.display.scroller, "dragover", funcs.over); toggle(cm.display.scroller, "dragleave", funcs.leave); toggle(cm.display.scroller, "drop", funcs.drop); } } function wrappingChanged(cm) { if (cm.options.lineWrapping) { addClass(cm.display.wrapper, "CodeMirror-wrap"); cm.display.sizer.style.minWidth = ""; cm.display.sizerWidth = null; } else { rmClass(cm.display.wrapper, "CodeMirror-wrap"); findMaxLine(cm); } estimateLineHeights(cm); regChange(cm); clearCaches(cm); setTimeout(function () { return updateScrollbars(cm); }, 100); } // A CodeMirror instance represents an editor. This is the object // that user code is usually dealing with. function CodeMirror(place, options) { var this$1 = this; if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } this.options = options = options ? copyObj(options) : {}; // Determine effective options based on given values and defaults. copyObj(defaults, options, false); setGuttersForLineNumbers(options); var doc = options.value; if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } else if (options.mode) { doc.modeOption = options.mode; } this.doc = doc; var input = new CodeMirror.inputStyles[options.inputStyle](this); var display = this.display = new Display(place, doc, input); display.wrapper.CodeMirror = this; updateGutters(this); themeChanged(this); if (options.lineWrapping) { this.display.wrapper.className += " CodeMirror-wrap"; } initScrollbars(this); this.state = { keyMaps: [], // stores maps added by addKeyMap overlays: [], // highlighting overlays, as added by addOverlay modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info overwrite: false, delayingBlurEvent: false, focused: false, suppressEdits: false, // used to disable editing during key handlers when in readOnly mode pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll selectingText: false, draggingText: false, highlight: new Delayed(), // stores highlight worker timeout keySeq: null, // Unfinished key sequence specialChars: null }; if (options.autofocus && !mobile) { display.input.focus(); } // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } registerEventHandlers(this); ensureGlobalHandlers(); startOperation(this); this.curOp.forceUpdate = true; attachDoc(this, doc); if ((options.autofocus && !mobile) || this.hasFocus()) { setTimeout(bind(onFocus, this), 20); } else { onBlur(this); } for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) { optionHandlers[opt](this$1, options[opt], Init); } } maybeUpdateLineNumberWidth(this); if (options.finishInit) { options.finishInit(this); } for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); } endOperation(this); // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. if (webkit && options.lineWrapping && getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") { display.lineDiv.style.textRendering = "auto"; } } // The default configuration options. CodeMirror.defaults = defaults; // Functions to run when options are changed. CodeMirror.optionHandlers = optionHandlers; // Attach the necessary event handlers when initializing the editor function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) { on(d.scroller, "dblclick", operation(cm, function (e) { if (signalDOMEvent(cm, e)) { return } var pos = posFromMouse(cm, e); if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); var word = cm.findWordAt(pos); extendSelection(cm.doc, word.anchor, word.head); })); } else { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } // Some browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); // Used to suppress mouse event handling when a touch happens var touchFinished, prevTouch = {end: 0}; function finishTouch() { if (d.activeTouch) { touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); prevTouch = d.activeTouch; prevTouch.end = +new Date; } } function isMouseLikeTouchEvent(e) { if (e.touches.length != 1) { return false } var touch = e.touches[0]; return touch.radiusX <= 1 && touch.radiusY <= 1 } function farAway(touch, other) { if (other.left == null) { return true } var dx = other.left - touch.left, dy = other.top - touch.top; return dx * dx + dy * dy > 20 * 20 } on(d.scroller, "touchstart", function (e) { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { d.input.ensurePolled(); clearTimeout(touchFinished); var now = +new Date; d.activeTouch = {start: now, moved: false, prev: now - prevTouch.end <= 300 ? prevTouch : null}; if (e.touches.length == 1) { d.activeTouch.left = e.touches[0].pageX; d.activeTouch.top = e.touches[0].pageY; } } }); on(d.scroller, "touchmove", function () { if (d.activeTouch) { d.activeTouch.moved = true; } }); on(d.scroller, "touchend", function (e) { var touch = d.activeTouch; if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { var pos = cm.coordsChar(d.activeTouch, "page"), range; if (!touch.prev || farAway(touch, touch.prev)) // Single tap { range = new Range(pos, pos); } else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap { range = cm.findWordAt(pos); } else // Triple tap { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } cm.setSelection(range.anchor, range.head); cm.focus(); e_preventDefault(e); } finishTouch(); }); on(d.scroller, "touchcancel", finishTouch); // Sync scrolling between fake scrollbars and real scrollable // area, ensure viewport is updated when scrolling. on(d.scroller, "scroll", function () { if (d.scroller.clientHeight) { updateScrollTop(cm, d.scroller.scrollTop); setScrollLeft(cm, d.scroller.scrollLeft, true); signal(cm, "scroll", cm); } }); // Listen to wheel events in order to try and update the viewport on time. on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); d.dragFunctions = { enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, start: function (e) { return onDragStart(cm, e); }, drop: operation(cm, onDrop), leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} }; var inp = d.input.getField(); on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); on(inp, "keydown", operation(cm, onKeyDown)); on(inp, "keypress", operation(cm, onKeyPress)); on(inp, "focus", function (e) { return onFocus(cm, e); }); on(inp, "blur", function (e) { return onBlur(cm, e); }); } var initHooks = []; CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; // Indent the given line. The how parameter can be "smart", // "add"/null, "subtract", or "prev". When aggressive is false // (typically set to true for forced single-line indents), empty // lines are not indented, and places where the mode returns Pass // are left alone. function indentLine(cm, n, how, aggressive) { var doc = cm.doc, state; if (how == null) { how = "add"; } if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation // method. if (!doc.mode.indent) { how = "prev"; } else { state = getContextBefore(cm, n).state; } } var tabSize = cm.options.tabSize; var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); if (line.stateAfter) { line.stateAfter = null; } var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (!aggressive && !/\S/.test(line.text)) { indentation = 0; how = "not"; } else if (how == "smart") { indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass || indentation > 150) { if (!aggressive) { return } how = "prev"; } } if (how == "prev") { if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } else { indentation = 0; } } else if (how == "add") { indentation = curSpace + cm.options.indentUnit; } else if (how == "subtract") { indentation = curSpace - cm.options.indentUnit; } else if (typeof how == "number") { indentation = curSpace + how; } indentation = Math.max(0, indentation); var indentString = "", pos = 0; if (cm.options.indentWithTabs) { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } if (pos < indentation) { indentString += spaceStr(indentation - pos); } if (indentString != curSpaceString) { replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; return true } else { // Ensure that, if the cursor was in the whitespace at the start // of the line, it is moved to the end of that space. for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { var range = doc.sel.ranges[i$1]; if (range.head.line == n && range.head.ch < curSpaceString.length) { var pos$1 = Pos(n, curSpaceString.length); replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); break } } } } // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied // text was made out of. var lastCopied = null; function setLastCopied(newLastCopied) { lastCopied = newLastCopied; } function applyTextInput(cm, inserted, deleted, sel, origin) { var doc = cm.doc; cm.display.shift = false; if (!sel) { sel = doc.sel; } var paste = cm.state.pasteIncoming || origin == "paste"; var textLines = splitLinesAuto(inserted), multiPaste = null; // When pasting N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; for (var i = 0; i < lastCopied.text.length; i++) { multiPaste.push(doc.splitLines(lastCopied.text[i])); } } } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { multiPaste = map(textLines, function (l) { return [l]; }); } } var updateInput; // Normal behavior is to insert the new text into every selection for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { var range$$1 = sel.ranges[i$1]; var from = range$$1.from(), to = range$$1.to(); if (range$$1.empty()) { if (deleted && deleted > 0) // Handle deletion { from = Pos(from.line, from.ch - deleted); } else if (cm.state.overwrite && !paste) // Handle overwrite { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) { from = to = Pos(from.line, 0); } } updateInput = cm.curOp.updateInput; var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); } if (inserted && !paste) { triggerElectric(cm, inserted); } ensureCursorVisible(cm); cm.curOp.updateInput = updateInput; cm.curOp.typing = true; cm.state.pasteIncoming = cm.state.cutIncoming = false; } function handlePaste(e, cm) { var pasted = e.clipboardData && e.clipboardData.getData("Text"); if (pasted) { e.preventDefault(); if (!cm.isReadOnly() && !cm.options.disableInput) { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } return true } } function triggerElectric(cm, inserted) { // When an 'electric' character is inserted, immediately trigger a reindent if (!cm.options.electricChars || !cm.options.smartIndent) { return } var sel = cm.doc.sel; for (var i = sel.ranges.length - 1; i >= 0; i--) { var range$$1 = sel.ranges[i]; if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue } var mode = cm.getModeAt(range$$1.head); var indented = false; if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { indented = indentLine(cm, range$$1.head.line, "smart"); break } } } else if (mode.electricInput) { if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch))) { indented = indentLine(cm, range$$1.head.line, "smart"); } } if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); } } } function copyableRanges(cm) { var text = [], ranges = []; for (var i = 0; i < cm.doc.sel.ranges.length; i++) { var line = cm.doc.sel.ranges[i].head.line; var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; ranges.push(lineRange); text.push(cm.getRange(lineRange.anchor, lineRange.head)); } return {text: text, ranges: ranges} } function disableBrowserMagic(field, spellcheck) { field.setAttribute("autocorrect", "off"); field.setAttribute("autocapitalize", "off"); field.setAttribute("spellcheck", !!spellcheck); } function hiddenTextarea() { var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling // our fake cursor out of view. On webkit, when wrap=off, paste is // very slow. So make the area wide instead. if (webkit) { te.style.width = "1000px"; } else { te.setAttribute("wrap", "off"); } // If border: 0; -- iOS fails to open keyboard (issue #1287) if (ios) { te.style.border = "1px solid black"; } disableBrowserMagic(te); return div } // The publicly visible API. Note that methodOp(f) means // 'wrap f in an operation, performed on its `this` parameter'. // This is not the complete set of editor methods. Most of the // methods defined on the Doc type are also injected into // CodeMirror.prototype, for backwards compatibility and // convenience. function addEditorMethods(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; var helpers = CodeMirror.helpers = {}; CodeMirror.prototype = { constructor: CodeMirror, focus: function(){window.focus(); this.display.input.focus();}, setOption: function(option, value) { var options = this.options, old = options[option]; if (options[option] == value && option != "mode") { return } options[option] = value; if (optionHandlers.hasOwnProperty(option)) { operation(this, optionHandlers[option])(this, value, old); } signal(this, "optionChange", this, option); }, getOption: function(option) {return this.options[option]}, getDoc: function() {return this.doc}, addKeyMap: function(map$$1, bottom) { this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1)); }, removeKeyMap: function(map$$1) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) { if (maps[i] == map$$1 || maps[i].name == map$$1) { maps.splice(i, 1); return true } } }, addOverlay: methodOp(function(spec, options) { var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); if (mode.startState) { throw new Error("Overlays may not be stateful.") } insertSorted(this.state.overlays, {mode: mode, modeSpec: spec, opaque: options && options.opaque, priority: (options && options.priority) || 0}, function (overlay) { return overlay.priority; }); this.state.modeGen++; regChange(this); }), removeOverlay: methodOp(function(spec) { var this$1 = this; var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { var cur = overlays[i].modeSpec; if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); this$1.state.modeGen++; regChange(this$1); return } } }), indentLine: methodOp(function(n, dir, aggressive) { if (typeof dir != "string" && typeof dir != "number") { if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } else { dir = dir ? "add" : "subtract"; } } if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } }), indentSelection: methodOp(function(how) { var this$1 = this; var ranges = this.doc.sel.ranges, end = -1; for (var i = 0; i < ranges.length; i++) { var range$$1 = ranges[i]; if (!range$$1.empty()) { var from = range$$1.from(), to = range$$1.to(); var start = Math.max(end, from.line); end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; for (var j = start; j < end; ++j) { indentLine(this$1, j, how); } var newRanges = this$1.doc.sel.ranges; if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } } else if (range$$1.head.line > end) { indentLine(this$1, range$$1.head.line, how, true); end = range$$1.head.line; if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); } } } }), // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(pos, precise) { return takeToken(this, pos, precise) }, getLineTokens: function(line, precise) { return takeToken(this, Pos(line), precise, true) }, getTokenTypeAt: function(pos) { pos = clipPos(this.doc, pos); var styles = getLineStyles(this, getLine(this.doc, pos.line)); var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; var type; if (ch == 0) { type = styles[2]; } else { for (;;) { var mid = (before + after) >> 1; if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } else { type = styles[mid * 2 + 2]; break } } } var cut = type ? type.indexOf("overlay ") : -1; return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) }, getModeAt: function(pos) { var mode = this.doc.mode; if (!mode.innerMode) { return mode } return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode }, getHelper: function(pos, type) { return this.getHelpers(pos, type)[0] }, getHelpers: function(pos, type) { var this$1 = this; var found = []; if (!helpers.hasOwnProperty(type)) { return found } var help = helpers[type], mode = this.getModeAt(pos); if (typeof mode[type] == "string") { if (help[mode[type]]) { found.push(help[mode[type]]); } } else if (mode[type]) { for (var i = 0; i < mode[type].length; i++) { var val = help[mode[type][i]]; if (val) { found.push(val); } } } else if (mode.helperType && help[mode.helperType]) { found.push(help[mode.helperType]); } else if (help[mode.name]) { found.push(help[mode.name]); } for (var i$1 = 0; i$1 < help._global.length; i$1++) { var cur = help._global[i$1]; if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) { found.push(cur.val); } } return found }, getStateAfter: function(line, precise) { var doc = this.doc; line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); return getContextBefore(this, line + 1, precise).state }, cursorCoords: function(start, mode) { var pos, range$$1 = this.doc.sel.primary(); if (start == null) { pos = range$$1.head; } else if (typeof start == "object") { pos = clipPos(this.doc, start); } else { pos = start ? range$$1.from() : range$$1.to(); } return cursorCoords(this, pos, mode || "page") }, charCoords: function(pos, mode) { return charCoords(this, clipPos(this.doc, pos), mode || "page") }, coordsChar: function(coords, mode) { coords = fromCoordSystem(this, coords, mode || "page"); return coordsChar(this, coords.left, coords.top) }, lineAtHeight: function(height, mode) { height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; return lineAtHeight(this.doc, height + this.display.viewOffset) }, heightAtLine: function(line, mode, includeWidgets) { var end = false, lineObj; if (typeof line == "number") { var last = this.doc.first + this.doc.size - 1; if (line < this.doc.first) { line = this.doc.first; } else if (line > last) { line = last; end = true; } lineObj = getLine(this.doc, line); } else { lineObj = line; } return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + (end ? this.doc.height - heightAtLine(lineObj) : 0) }, defaultTextHeight: function() { return textHeight(this.display) }, defaultCharWidth: function() { return charWidth(this.display) }, getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, addWidget: function(pos, node, scroll, vert, horiz) { var display = this.display; pos = cursorCoords(this, clipPos(this.doc, pos)); var top = pos.bottom, left = pos.left; node.style.position = "absolute"; node.setAttribute("cm-ignore-events", "true"); this.display.input.setUneditable(node); display.sizer.appendChild(node); if (vert == "over") { top = pos.top; } else if (vert == "above" || vert == "near") { var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); // Default to positioning above (if specified and possible); otherwise default to positioning below if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) { top = pos.top - node.offsetHeight; } else if (pos.bottom + node.offsetHeight <= vspace) { top = pos.bottom; } if (left + node.offsetWidth > hspace) { left = hspace - node.offsetWidth; } } node.style.top = top + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { left = display.sizer.clientWidth - node.offsetWidth; node.style.right = "0px"; } else { if (horiz == "left") { left = 0; } else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } node.style.left = left + "px"; } if (scroll) { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } }, triggerOnKeyDown: methodOp(onKeyDown), triggerOnKeyPress: methodOp(onKeyPress), triggerOnKeyUp: onKeyUp, triggerOnMouseDown: methodOp(onMouseDown), execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) { return commands[cmd].call(null, this) } }, triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), findPosH: function(from, amount, unit, visually) { var this$1 = this; var dir = 1; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { cur = findPosH(this$1.doc, cur, dir, unit, visually); if (cur.hitSide) { break } } return cur }, moveH: methodOp(function(dir, unit) { var this$1 = this; this.extendSelectionsBy(function (range$$1) { if (this$1.display.shift || this$1.doc.extend || range$$1.empty()) { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) } else { return dir < 0 ? range$$1.from() : range$$1.to() } }, sel_move); }), deleteH: methodOp(function(dir, unit) { var sel = this.doc.sel, doc = this.doc; if (sel.somethingSelected()) { doc.replaceSelection("", null, "+delete"); } else { deleteNearSelection(this, function (range$$1) { var other = findPosH(doc, range$$1.head, dir, unit, false); return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other} }); } }), findPosV: function(from, amount, unit, goalColumn) { var this$1 = this; var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { var coords = cursorCoords(this$1, cur, "div"); if (x == null) { x = coords.left; } else { coords.left = x; } cur = findPosV(this$1, coords, dir, unit); if (cur.hitSide) { break } } return cur }, moveV: methodOp(function(dir, unit) { var this$1 = this; var doc = this.doc, goals = []; var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); doc.extendSelectionsBy(function (range$$1) { if (collapse) { return dir < 0 ? range$$1.from() : range$$1.to() } var headPos = cursorCoords(this$1, range$$1.head, "div"); if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; } goals.push(headPos.left); var pos = findPosV(this$1, headPos, dir, unit); if (unit == "page" && range$$1 == doc.sel.primary()) { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } return pos }, sel_move); if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) { doc.sel.ranges[i].goalColumn = goals[i]; } } }), // Find the word at the given position (as returned by coordsChar). findWordAt: function(pos) { var doc = this.doc, line = getLine(doc, pos.line).text; var start = pos.ch, end = pos.ch; if (line) { var helper = this.getHelper(pos, "wordChars"); if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } var startChar = line.charAt(start); var check = isWordChar(startChar, helper) ? function (ch) { return isWordChar(ch, helper); } : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; while (start > 0 && check(line.charAt(start - 1))) { --start; } while (end < line.length && check(line.charAt(end))) { ++end; } } return new Range(Pos(pos.line, start), Pos(pos.line, end)) }, toggleOverwrite: function(value) { if (value != null && value == this.state.overwrite) { return } if (this.state.overwrite = !this.state.overwrite) { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } else { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } signal(this, "overwriteToggle", this, this.state.overwrite); }, hasFocus: function() { return this.display.input.getField() == activeElt() }, isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), getScrollInfo: function() { var scroller = this.display.scroller; return {left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, clientHeight: displayHeight(this), clientWidth: displayWidth(this)} }, scrollIntoView: methodOp(function(range$$1, margin) { if (range$$1 == null) { range$$1 = {from: this.doc.sel.primary().head, to: null}; if (margin == null) { margin = this.options.cursorScrollMargin; } } else if (typeof range$$1 == "number") { range$$1 = {from: Pos(range$$1, 0), to: null}; } else if (range$$1.from == null) { range$$1 = {from: range$$1, to: null}; } if (!range$$1.to) { range$$1.to = range$$1.from; } range$$1.margin = margin || 0; if (range$$1.from.line != null) { scrollToRange(this, range$$1); } else { scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin); } }), setSize: methodOp(function(width, height) { var this$1 = this; var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; if (width != null) { this.display.wrapper.style.width = interpret(width); } if (height != null) { this.display.wrapper.style.height = interpret(height); } if (this.options.lineWrapping) { clearLineMeasurementCache(this); } var lineNo$$1 = this.display.viewFrom; this.doc.iter(lineNo$$1, this.display.viewTo, function (line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } } ++lineNo$$1; }); this.curOp.forceUpdate = true; signal(this, "refresh", this); }), operation: function(f){return runInOp(this, f)}, startOperation: function(){return startOperation(this)}, endOperation: function(){return endOperation(this)}, refresh: methodOp(function() { var oldHeight = this.display.cachedTextHeight; regChange(this); this.curOp.forceUpdate = true; clearCaches(this); scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); updateGutterSpace(this); if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) { estimateLineHeights(this); } signal(this, "refresh", this); }), swapDoc: methodOp(function(doc) { var old = this.doc; old.cm = null; attachDoc(this, doc); clearCaches(this); this.display.input.reset(); scrollToCoords(this, doc.scrollLeft, doc.scrollTop); this.curOp.forceScroll = true; signalLater(this, "swapDoc", this, old); return old }), phrase: function(phraseText) { var phrases = this.options.phrases; return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText }, getInputField: function(){return this.display.input.getField()}, getWrapperElement: function(){return this.display.wrapper}, getScrollerElement: function(){return this.display.scroller}, getGutterElement: function(){return this.display.gutters} }; eventMixin(CodeMirror); CodeMirror.registerHelper = function(type, name, value) { if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } helpers[type][name] = value; }; CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { CodeMirror.registerHelper(type, name, value); helpers[type]._global.push({pred: predicate, val: value}); }; } // Used for horizontal relative motion. Dir is -1 or 1 (left or // right), unit can be "char", "column" (like char, but doesn't // cross line boundaries), "word" (across next word), or "group" (to // the start of next group of word or non-word-non-whitespace // chars). The visually param controls whether, in right-to-left // text, direction 1 means to move towards the next index in the // string, or towards the character to the right of the current // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { var oldPos = pos; var origDir = dir; var lineObj = getLine(doc, pos.line); function findNextLine() { var l = pos.line + dir; if (l < doc.first || l >= doc.first + doc.size) { return false } pos = new Pos(l, pos.ch, pos.sticky); return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { var next; if (visually) { next = moveVisually(doc.cm, lineObj, pos, dir); } else { next = moveLogically(lineObj, pos, dir); } if (next == null) { if (!boundToLine && findNextLine()) { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); } else { return false } } else { pos = next; } return true } if (unit == "char") { moveOnce(); } else if (unit == "column") { moveOnce(true); } else if (unit == "word" || unit == "group") { var sawType = null, group = unit == "group"; var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); for (var first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) { break } var cur = lineObj.text.charAt(pos.ch) || "\n"; var type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null : "p"; if (group && !first && !type) { type = "s"; } if (sawType && sawType != type) { if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} break } if (type) { sawType = type; } if (dir > 0 && !moveOnce(!first)) { break } } } var result = skipAtomic(doc, pos, oldPos, origDir, true); if (equalCursorPos(oldPos, result)) { result.hitSide = true; } return result } // For relative vertical movement. Dir may be -1 or 1. Unit can be // "page" or "line". The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosV(cm, pos, dir, unit) { var doc = cm.doc, x = pos.left, y; if (unit == "page") { var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } var target; for (;;) { target = coordsChar(cm, x, y); if (!target.outside) { break } if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } y += dir * 5; } return target } // CONTENTEDITABLE INPUT STYLE var ContentEditableInput = function(cm) { this.cm = cm; this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; this.polling = new Delayed(); this.composing = null; this.gracePeriod = false; this.readDOMTimeout = null; }; ContentEditableInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = input.cm; var div = input.div = display.lineDiv; disableBrowserMagic(div, cm.options.spellcheck); on(div, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } // IE doesn't fire input events, so we schedule a read for the pasted content in this way if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } }); on(div, "compositionstart", function (e) { this$1.composing = {data: e.data, done: false}; }); on(div, "compositionupdate", function (e) { if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } }); on(div, "compositionend", function (e) { if (this$1.composing) { if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } this$1.composing.done = true; } }); on(div, "touchstart", function () { return input.forceCompositionEnd(); }); on(div, "input", function () { if (!this$1.composing) { this$1.readFromDOMSoon(); } }); function onCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.operation(function () { cm.setSelections(ranges.ranges, 0, sel_dontScroll); cm.replaceSelection("", null, "cut"); }); } } if (e.clipboardData) { e.clipboardData.clearData(); var content = lastCopied.text.join("\n"); // iOS exposes the clipboard API, but seems to discard content inserted into it e.clipboardData.setData("Text", content); if (e.clipboardData.getData("Text") == content) { e.preventDefault(); return } } // Old-fashioned briefly-focus-a-textarea hack var kludge = hiddenTextarea(), te = kludge.firstChild; cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); te.value = lastCopied.text.join("\n"); var hadFocus = document.activeElement; selectInput(te); setTimeout(function () { cm.display.lineSpace.removeChild(kludge); hadFocus.focus(); if (hadFocus == div) { input.showPrimarySelection(); } }, 50); } on(div, "copy", onCopyCut); on(div, "cut", onCopyCut); }; ContentEditableInput.prototype.prepareSelection = function () { var result = prepareSelection(this.cm, false); result.focus = this.cm.state.focused; return result }; ContentEditableInput.prototype.showSelection = function (info, takeFocus) { if (!info || !this.cm.display.view.length) { return } if (info.focus || takeFocus) { this.showPrimarySelection(); } this.showMultipleSelections(info); }; ContentEditableInput.prototype.getSelection = function () { return this.cm.display.wrapper.ownerDocument.getSelection() }; ContentEditableInput.prototype.showPrimarySelection = function () { var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); var from = prim.from(), to = prim.to(); if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { sel.removeAllRanges(); return } var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && cmp(minPos(curAnchor, curFocus), from) == 0 && cmp(maxPos(curAnchor, curFocus), to) == 0) { return } var view = cm.display.view; var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || {node: view[0].measure.map[2], offset: 0}; var end = to.line < cm.display.viewTo && posToDOM(cm, to); if (!end) { var measure = view[view.length - 1].measure; var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}; } if (!start || !end) { sel.removeAllRanges(); return } var old = sel.rangeCount && sel.getRangeAt(0), rng; try { rng = range(start.node, start.offset, end.offset, end.node); } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { if (!gecko && cm.state.focused) { sel.collapse(start.node, start.offset); if (!rng.collapsed) { sel.removeAllRanges(); sel.addRange(rng); } } else { sel.removeAllRanges(); sel.addRange(rng); } if (old && sel.anchorNode == null) { sel.addRange(old); } else if (gecko) { this.startGracePeriod(); } } this.rememberSelection(); }; ContentEditableInput.prototype.startGracePeriod = function () { var this$1 = this; clearTimeout(this.gracePeriod); this.gracePeriod = setTimeout(function () { this$1.gracePeriod = false; if (this$1.selectionChanged()) { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } }, 20); }; ContentEditableInput.prototype.showMultipleSelections = function (info) { removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); }; ContentEditableInput.prototype.rememberSelection = function () { var sel = this.getSelection(); this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; }; ContentEditableInput.prototype.selectionInEditor = function () { var sel = this.getSelection(); if (!sel.rangeCount) { return false } var node = sel.getRangeAt(0).commonAncestorContainer; return contains(this.div, node) }; ContentEditableInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor") { if (!this.selectionInEditor()) { this.showSelection(this.prepareSelection(), true); } this.div.focus(); } }; ContentEditableInput.prototype.blur = function () { this.div.blur(); }; ContentEditableInput.prototype.getField = function () { return this.div }; ContentEditableInput.prototype.supportsTouch = function () { return true }; ContentEditableInput.prototype.receivedFocus = function () { var input = this; if (this.selectionInEditor()) { this.pollSelection(); } else { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } function poll() { if (input.cm.state.focused) { input.pollSelection(); input.polling.set(input.cm.options.pollInterval, poll); } } this.polling.set(this.cm.options.pollInterval, poll); }; ContentEditableInput.prototype.selectionChanged = function () { var sel = this.getSelection(); return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset }; ContentEditableInput.prototype.pollSelection = function () { if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } var sel = this.getSelection(), cm = this.cm; // On Android Chrome (version 56, at least), backspacing into an // uneditable block element will put the cursor in that element, // and then, because it's not editable, hide the virtual keyboard. // Because Android doesn't allow us to actually detect backspace // presses in a sane way, this code checks for when that happens // and simulates a backspace press in this case. if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) { this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); this.blur(); this.focus(); return } if (this.composing) { return } this.rememberSelection(); var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var head = domToPos(cm, sel.focusNode, sel.focusOffset); if (anchor && head) { runInOp(cm, function () { setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } }); } }; ContentEditableInput.prototype.pollContent = function () { if (this.readDOMTimeout != null) { clearTimeout(this.readDOMTimeout); this.readDOMTimeout = null; } var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); var from = sel.from(), to = sel.to(); if (from.ch == 0 && from.line > cm.firstLine()) { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) { to = Pos(to.line + 1, 0); } if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } var fromIndex, fromLine, fromNode; if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { fromLine = lineNo(display.view[0].line); fromNode = display.view[0].node; } else { fromLine = lineNo(display.view[fromIndex].line); fromNode = display.view[fromIndex - 1].node.nextSibling; } var toIndex = findViewIndex(cm, to.line); var toLine, toNode; if (toIndex == display.view.length - 1) { toLine = display.viewTo - 1; toNode = display.lineDiv.lastChild; } else { toLine = lineNo(display.view[toIndex + 1].line) - 1; toNode = display.view[toIndex + 1].node.previousSibling; } if (!fromNode) { return false } var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); while (newText.length > 1 && oldText.length > 1) { if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } else { break } } var cutFront = 0, cutEnd = 0; var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) { ++cutFront; } var newBot = lst(newText), oldBot = lst(oldText); var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), oldBot.length - (oldText.length == 1 ? cutFront : 0)); while (cutEnd < maxCutEnd && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { ++cutEnd; } // Try to move start of change to start of selection if ambiguous if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { while (cutFront && cutFront > from.ch && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { cutFront--; cutEnd++; } } newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); var chFrom = Pos(fromLine, cutFront); var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { replaceRange(cm.doc, newText, chFrom, chTo, "+input"); return true } }; ContentEditableInput.prototype.ensurePolled = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.reset = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.forceCompositionEnd = function () { if (!this.composing) { return } clearTimeout(this.readDOMTimeout); this.composing = null; this.updateFromDOM(); this.div.blur(); this.div.focus(); }; ContentEditableInput.prototype.readFromDOMSoon = function () { var this$1 = this; if (this.readDOMTimeout != null) { return } this.readDOMTimeout = setTimeout(function () { this$1.readDOMTimeout = null; if (this$1.composing) { if (this$1.composing.done) { this$1.composing = null; } else { return } } this$1.updateFromDOM(); }, 80); }; ContentEditableInput.prototype.updateFromDOM = function () { var this$1 = this; if (this.cm.isReadOnly() || !this.pollContent()) { runInOp(this.cm, function () { return regChange(this$1.cm); }); } }; ContentEditableInput.prototype.setUneditable = function (node) { node.contentEditable = "false"; }; ContentEditableInput.prototype.onKeyPress = function (e) { if (e.charCode == 0 || this.composing) { return } e.preventDefault(); if (!this.cm.isReadOnly()) { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } }; ContentEditableInput.prototype.readOnlyChanged = function (val) { this.div.contentEditable = String(val != "nocursor"); }; ContentEditableInput.prototype.onContextMenu = function () {}; ContentEditableInput.prototype.resetPosition = function () {}; ContentEditableInput.prototype.needsContentAttribute = true; function posToDOM(cm, pos) { var view = findViewForLine(cm, pos.line); if (!view || view.hidden) { return null } var line = getLine(cm.doc, pos.line); var info = mapFromLineView(view, line, pos.line); var order = getOrder(line, cm.doc.direction), side = "left"; if (order) { var partPos = getBidiPartAt(order, pos.ch); side = partPos % 2 ? "right" : "left"; } var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); result.offset = result.collapse == "right" ? result.end : result.start; return result } function isInGutter(node) { for (var scan = node; scan; scan = scan.parentNode) { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } return false } function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } function domTextBetween(cm, from, to, fromLine, toLine) { var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; function recognizeMarker(id) { return function (marker) { return marker.id == id; } } function close() { if (closing) { text += lineSep; if (extraLinebreak) { text += lineSep; } closing = extraLinebreak = false; } } function addText(str) { if (str) { close(); text += str; } } function walk(node) { if (node.nodeType == 1) { var cmText = node.getAttribute("cm-text"); if (cmText) { addText(cmText); return } var markerID = node.getAttribute("cm-marker"), range$$1; if (markerID) { var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); if (found.length && (range$$1 = found[0].find(0))) { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); } return } if (node.getAttribute("contenteditable") == "false") { return } var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } if (isBlock) { close(); } for (var i = 0; i < node.childNodes.length; i++) { walk(node.childNodes[i]); } if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } if (isBlock) { closing = true; } } else if (node.nodeType == 3) { addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); } } for (;;) { walk(from); if (from == to) { break } from = from.nextSibling; extraLinebreak = false; } return text } function domToPos(cm, node, offset) { var lineNode; if (node == cm.display.lineDiv) { lineNode = cm.display.lineDiv.childNodes[offset]; if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } node = null; offset = 0; } else { for (lineNode = node;; lineNode = lineNode.parentNode) { if (!lineNode || lineNode == cm.display.lineDiv) { return null } if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } } } for (var i = 0; i < cm.display.view.length; i++) { var lineView = cm.display.view[i]; if (lineView.node == lineNode) { return locateNodeInLineView(lineView, node, offset) } } } function locateNodeInLineView(lineView, node, offset) { var wrapper = lineView.text.firstChild, bad = false; if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } if (node == wrapper) { bad = true; node = wrapper.childNodes[offset]; offset = 0; if (!node) { var line = lineView.rest ? lst(lineView.rest) : lineView.line; return badPos(Pos(lineNo(line), line.text.length), bad) } } var textNode = node.nodeType == 3 ? node : null, topNode = node; if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { textNode = node.firstChild; if (offset) { offset = textNode.nodeValue.length; } } while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } var measure = lineView.measure, maps = measure.maps; function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { var map$$1 = i < 0 ? measure.map : maps[i]; for (var j = 0; j < map$$1.length; j += 3) { var curNode = map$$1[j + 2]; if (curNode == textNode || curNode == topNode) { var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); var ch = map$$1[j] + offset; if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; } return Pos(line, ch) } } } } var found = find(textNode, topNode, offset); if (found) { return badPos(found, bad) } // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { found = find(after, after.firstChild, 0); if (found) { return badPos(Pos(found.line, found.ch - dist), bad) } else { dist += after.textContent.length; } } for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { found = find(before, before.firstChild, -1); if (found) { return badPos(Pos(found.line, found.ch + dist$1), bad) } else { dist$1 += before.textContent.length; } } } // TEXTAREA INPUT STYLE var TextareaInput = function(cm) { this.cm = cm; // See input.poll and input.reset this.prevInput = ""; // Flag that indicates whether we expect input to appear real soon // now (after some event like 'keypress' or 'input') and are // polling intensively. this.pollingFast = false; // Self-resetting timeout for the poller this.polling = new Delayed(); // Used to work around IE issue with selection being forgotten when focus moves away from textarea this.hasSelection = false; this.composing = null; }; TextareaInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = this.cm; this.createField(display); var te = this.textarea; display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) if (ios) { te.style.width = "0px"; } on(te, "input", function () { if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } input.poll(); }); on(te, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } cm.state.pasteIncoming = true; input.fastPoll(); }); function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll); } else { input.prevInput = ""; te.value = ranges.text.join("\n"); selectInput(te); } } if (e.type == "cut") { cm.state.cutIncoming = true; } } on(te, "cut", prepareCopyCut); on(te, "copy", prepareCopyCut); on(display.scroller, "paste", function (e) { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } cm.state.pasteIncoming = true; input.focus(); }); // Prevent normal selection in the editor (we handle our own) on(display.lineSpace, "selectstart", function (e) { if (!eventInWidget(display, e)) { e_preventDefault(e); } }); on(te, "compositionstart", function () { var start = cm.getCursor("from"); if (input.composing) { input.composing.range.clear(); } input.composing = { start: start, range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) }; }); on(te, "compositionend", function () { if (input.composing) { input.poll(); input.composing.range.clear(); input.composing = null; } }); }; TextareaInput.prototype.createField = function (_display) { // Wraps and hides input textarea this.wrapper = hiddenTextarea(); // The semihidden textarea that is focused when the editor is // focused, and receives input. this.textarea = this.wrapper.firstChild; }; TextareaInput.prototype.prepareSelection = function () { // Redraw the selection and/or cursor var cm = this.cm, display = cm.display, doc = cm.doc; var result = prepareSelection(cm); // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)); result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)); } return result }; TextareaInput.prototype.showSelection = function (drawn) { var cm = this.cm, display = cm.display; removeChildrenAndAdd(display.cursorDiv, drawn.cursors); removeChildrenAndAdd(display.selectionDiv, drawn.selection); if (drawn.teTop != null) { this.wrapper.style.top = drawn.teTop + "px"; this.wrapper.style.left = drawn.teLeft + "px"; } }; // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) TextareaInput.prototype.reset = function (typing) { if (this.contextMenuPending || this.composing) { return } var cm = this.cm; if (cm.somethingSelected()) { this.prevInput = ""; var content = cm.getSelection(); this.textarea.value = content; if (cm.state.focused) { selectInput(this.textarea); } if (ie && ie_version >= 9) { this.hasSelection = content; } } else if (!typing) { this.prevInput = this.textarea.value = ""; if (ie && ie_version >= 9) { this.hasSelection = null; } } }; TextareaInput.prototype.getField = function () { return this.textarea }; TextareaInput.prototype.supportsTouch = function () { return false }; TextareaInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { try { this.textarea.focus(); } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } }; TextareaInput.prototype.blur = function () { this.textarea.blur(); }; TextareaInput.prototype.resetPosition = function () { this.wrapper.style.top = this.wrapper.style.left = 0; }; TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. TextareaInput.prototype.slowPoll = function () { var this$1 = this; if (this.pollingFast) { return } this.polling.set(this.cm.options.pollInterval, function () { this$1.poll(); if (this$1.cm.state.focused) { this$1.slowPoll(); } }); }; // When an event has just come in that is likely to add or change // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. TextareaInput.prototype.fastPoll = function () { var missed = false, input = this; input.pollingFast = true; function p() { var changed = input.poll(); if (!changed && !missed) {missed = true; input.polling.set(60, p);} else {input.pollingFast = false; input.slowPoll();} } input.polling.set(20, p); }; // Read input from the textarea, and update the document to match. // When something is selected, it is present in the textarea, and // selected (unless it is huge, in which case a placeholder is // used). When nothing is selected, the cursor sits after previously // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). TextareaInput.prototype.poll = function () { var this$1 = this; var cm = this.cm, input = this.textarea, prevInput = this.prevInput; // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, // in which case reading its value would be expensive. if (this.contextMenuPending || !cm.state.focused || (hasSelection(input) && !prevInput && !this.composing) || cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) { return false } var text = input.value; // If nothing changed, bail. if (text == prevInput && !cm.somethingSelected()) { return false } // Work around nonsensical selection resetting in IE9/10, and // inexplicable appearance of private area unicode characters on // some key combos in Mac (#2689). if (ie && ie_version >= 9 && this.hasSelection === text || mac && /[\uf700-\uf7ff]/.test(text)) { cm.display.input.reset(); return false } if (cm.doc.sel == cm.display.selForContextMenu) { var first = text.charCodeAt(0); if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } } // Find the part of the input that is actually new var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } runInOp(cm, function () { applyTextInput(cm, text.slice(same), prevInput.length - same, null, this$1.composing ? "*compose" : null); // Don't leave long text in the textarea, since it makes further polling slow if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } else { this$1.prevInput = text; } if (this$1.composing) { this$1.composing.range.clear(); this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), {className: "CodeMirror-composing"}); } }); return true }; TextareaInput.prototype.ensurePolled = function () { if (this.pollingFast && this.poll()) { this.pollingFast = false; } }; TextareaInput.prototype.onKeyPress = function () { if (ie && ie_version >= 9) { this.hasSelection = null; } this.fastPoll(); }; TextareaInput.prototype.onContextMenu = function (e) { var input = this, cm = input.cm, display = cm.display, te = input.textarea; var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; if (!pos || presto) { return } // Opera is difficult. // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; if (reset && cm.doc.sel.contains(pos) == -1) { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; input.wrapper.style.cssText = "position: absolute"; var wrapperBox = input.wrapper.getBoundingClientRect(); te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; var oldScrollY; if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) display.input.focus(); if (webkit) { window.scrollTo(null, oldScrollY); } display.input.reset(); // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } input.contextMenuPending = true; display.selForContextMenu = cm.doc.sel; clearTimeout(display.detectingSelectAll); // Select-all will be greyed out if there's nothing to select, so // this adds a zero-width space so that we can later check whether // it got selected. function prepareSelectAllHack() { if (te.selectionStart != null) { var selected = cm.somethingSelected(); var extval = "\u200b" + (selected ? te.value : ""); te.value = "\u21da"; // Used to catch context-menu undo te.value = extval; input.prevInput = selected ? "" : "\u200b"; te.selectionStart = 1; te.selectionEnd = extval.length; // Re-set this, in case some other handler touched the // selection in the meantime. display.selForContextMenu = cm.doc.sel; } } function rehide() { input.contextMenuPending = false; input.wrapper.style.cssText = oldWrapperCSS; te.style.cssText = oldCSS; if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } // Try to detect the user choosing select-all if (te.selectionStart != null) { if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } var i = 0, poll = function () { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") { operation(cm, selectAll)(cm); } else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500); } else { display.selForContextMenu = null; display.input.reset(); } }; display.detectingSelectAll = setTimeout(poll, 200); } } if (ie && ie_version >= 9) { prepareSelectAllHack(); } if (captureRightClick) { e_stop(e); var mouseup = function () { off(window, "mouseup", mouseup); setTimeout(rehide, 20); }; on(window, "mouseup", mouseup); } else { setTimeout(rehide, 50); } }; TextareaInput.prototype.readOnlyChanged = function (val) { if (!val) { this.reset(); } this.textarea.disabled = val == "nocursor"; }; TextareaInput.prototype.setUneditable = function () {}; TextareaInput.prototype.needsContentAttribute = false; function fromTextArea(textarea, options) { options = options ? copyObj(options) : {}; options.value = textarea.value; if (!options.tabindex && textarea.tabIndex) { options.tabindex = textarea.tabIndex; } if (!options.placeholder && textarea.placeholder) { options.placeholder = textarea.placeholder; } // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { var hasFocus = activeElt(); options.autofocus = hasFocus == textarea || textarea.getAttribute("autofocus") != null && hasFocus == document.body; } function save() {textarea.value = cm.getValue();} var realSubmit; if (textarea.form) { on(textarea.form, "submit", save); // Deplorable hack to make the submit method do the right thing. if (!options.leaveSubmitMethodAlone) { var form = textarea.form; realSubmit = form.submit; try { var wrappedSubmit = form.submit = function () { save(); form.submit = realSubmit; form.submit(); form.submit = wrappedSubmit; }; } catch(e) {} } } options.finishInit = function (cm) { cm.save = save; cm.getTextArea = function () { return textarea; }; cm.toTextArea = function () { cm.toTextArea = isNaN; // Prevent this from being ran twice save(); textarea.parentNode.removeChild(cm.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { off(textarea.form, "submit", save); if (typeof textarea.form.submit == "function") { textarea.form.submit = realSubmit; } } }; }; textarea.style.display = "none"; var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, options); return cm } function addLegacyProps(CodeMirror) { CodeMirror.off = off; CodeMirror.on = on; CodeMirror.wheelEventPixels = wheelEventPixels; CodeMirror.Doc = Doc; CodeMirror.splitLines = splitLinesAuto; CodeMirror.countColumn = countColumn; CodeMirror.findColumn = findColumn; CodeMirror.isWordChar = isWordCharBasic; CodeMirror.Pass = Pass; CodeMirror.signal = signal; CodeMirror.Line = Line; CodeMirror.changeEnd = changeEnd; CodeMirror.scrollbarModel = scrollbarModel; CodeMirror.Pos = Pos; CodeMirror.cmpPos = cmp; CodeMirror.modes = modes; CodeMirror.mimeModes = mimeModes; CodeMirror.resolveMode = resolveMode; CodeMirror.getMode = getMode; CodeMirror.modeExtensions = modeExtensions; CodeMirror.extendMode = extendMode; CodeMirror.copyState = copyState; CodeMirror.startState = startState; CodeMirror.innerMode = innerMode; CodeMirror.commands = commands; CodeMirror.keyMap = keyMap; CodeMirror.keyName = keyName; CodeMirror.isModifierKey = isModifierKey; CodeMirror.lookupKey = lookupKey; CodeMirror.normalizeKeyMap = normalizeKeyMap; CodeMirror.StringStream = StringStream; CodeMirror.SharedTextMarker = SharedTextMarker; CodeMirror.TextMarker = TextMarker; CodeMirror.LineWidget = LineWidget; CodeMirror.e_preventDefault = e_preventDefault; CodeMirror.e_stopPropagation = e_stopPropagation; CodeMirror.e_stop = e_stop; CodeMirror.addClass = addClass; CodeMirror.contains = contains; CodeMirror.rmClass = rmClass; CodeMirror.keyNames = keyNames; } // EDITOR CONSTRUCTOR defineOptions(CodeMirror); addEditorMethods(CodeMirror); // Set up methods on CodeMirror's prototype to redirect to the editor's document. var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) { CodeMirror.prototype[prop] = (function(method) { return function() {return method.apply(this.doc, arguments)} })(Doc.prototype[prop]); } } eventMixin(Doc); CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) CodeMirror.defineMode = function(name/*, mode, …*/) { if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } defineMode.apply(this, arguments); }; CodeMirror.defineMIME = defineMIME; // Minimal default mode. CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); CodeMirror.defineMIME("text/plain", "null"); // EXTENSIONS CodeMirror.defineExtension = function (name, func) { CodeMirror.prototype[name] = func; }; CodeMirror.defineDocExtension = function (name, func) { Doc.prototype[name] = func; }; CodeMirror.fromTextArea = fromTextArea; addLegacyProps(CodeMirror); CodeMirror.version = "5.41.0"; return CodeMirror; }))); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/clike/clike.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; function Context(indented, column, type, info, align, prev) { this.indented = indented; this.column = column; this.type = type; this.info = info; this.align = align; this.prev = prev; } function pushContext(state, col, type, info) { var indent = state.indented; if (state.context && state.context.type == "statement" && type != "statement") indent = state.context.indented; return state.context = new Context(indent, col, type, info, null, state.context); } function popContext(state) { var t = state.context.type; if (t == ")" || t == "]" || t == "}") state.indented = state.context.indented; return state.context = state.context.prev; } function typeBefore(stream, state, pos) { if (state.prevToken == "variable" || state.prevToken == "type") return true; if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true; if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true; } function isTopScope(context) { for (;;) { if (!context || context.type == "top") return true; if (context.type == "}" && context.prev.info != "namespace") return false; context = context.prev; } } CodeMirror.defineMode("clike", function(config, parserConfig) { var indentUnit = config.indentUnit, statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, dontAlignCalls = parserConfig.dontAlignCalls, keywords = parserConfig.keywords || {}, types = parserConfig.types || {}, builtin = parserConfig.builtin || {}, blockKeywords = parserConfig.blockKeywords || {}, defKeywords = parserConfig.defKeywords || {}, atoms = parserConfig.atoms || {}, hooks = parserConfig.hooks || {}, multiLineStrings = parserConfig.multiLineStrings, indentStatements = parserConfig.indentStatements !== false, indentSwitch = parserConfig.indentSwitch !== false, namespaceSeparator = parserConfig.namespaceSeparator, isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/, numberStart = parserConfig.numberStart || /[\d\.]/, number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i, isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/, isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/, // An optional function that takes a {string} token and returns true if it // should be treated as a builtin. isReservedIdentifier = parserConfig.isReservedIdentifier || false; var curPunc, isDefKeyword; function tokenBase(stream, state) { var ch = stream.next(); if (hooks[ch]) { var result = hooks[ch](stream, state); if (result !== false) return result; } if (ch == '"' || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); } if (isPunctuationChar.test(ch)) { curPunc = ch; return null; } if (numberStart.test(ch)) { stream.backUp(1) if (stream.match(number)) return "number" stream.next() } if (ch == "/") { if (stream.eat("*")) { state.tokenize = tokenComment; return tokenComment(stream, state); } if (stream.eat("/")) { stream.skipToEnd(); return "comment"; } } if (isOperatorChar.test(ch)) { while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {} return "operator"; } stream.eatWhile(isIdentifierChar); if (namespaceSeparator) while (stream.match(namespaceSeparator)) stream.eatWhile(isIdentifierChar); var cur = stream.current(); if (contains(keywords, cur)) { if (contains(blockKeywords, cur)) curPunc = "newstatement"; if (contains(defKeywords, cur)) isDefKeyword = true; return "keyword"; } if (contains(types, cur)) return "type"; if (contains(builtin, cur) || (isReservedIdentifier && isReservedIdentifier(cur))) { if (contains(blockKeywords, cur)) curPunc = "newstatement"; return "builtin"; } if (contains(atoms, cur)) return "atom"; return "variable"; } function tokenString(quote) { return function(stream, state) { var escaped = false, next, end = false; while ((next = stream.next()) != null) { if (next == quote && !escaped) {end = true; break;} escaped = !escaped && next == "\\"; } if (end || !(escaped || multiLineStrings)) state.tokenize = null; return "string"; }; } function tokenComment(stream, state) { var maybeEnd = false, ch; while (ch = stream.next()) { if (ch == "/" && maybeEnd) { state.tokenize = null; break; } maybeEnd = (ch == "*"); } return "comment"; } function maybeEOL(stream, state) { if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context)) state.typeAtEndOfLine = typeBefore(stream, state, stream.pos) } // Interface return { startState: function(basecolumn) { return { tokenize: null, context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false), indented: 0, startOfLine: true, prevToken: null }; }, token: function(stream, state) { var ctx = state.context; if (stream.sol()) { if (ctx.align == null) ctx.align = false; state.indented = stream.indentation(); state.startOfLine = true; } if (stream.eatSpace()) { maybeEOL(stream, state); return null; } curPunc = isDefKeyword = null; var style = (state.tokenize || tokenBase)(stream, state); if (style == "comment" || style == "meta") return style; if (ctx.align == null) ctx.align = true; if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false))) while (state.context.type == "statement") popContext(state); else if (curPunc == "{") pushContext(state, stream.column(), "}"); else if (curPunc == "[") pushContext(state, stream.column(), "]"); else if (curPunc == "(") pushContext(state, stream.column(), ")"); else if (curPunc == "}") { while (ctx.type == "statement") ctx = popContext(state); if (ctx.type == "}") ctx = popContext(state); while (ctx.type == "statement") ctx = popContext(state); } else if (curPunc == ctx.type) popContext(state); else if (indentStatements && (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") || (ctx.type == "statement" && curPunc == "newstatement"))) { pushContext(state, stream.column(), "statement", stream.current()); } if (style == "variable" && ((state.prevToken == "def" || (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) && isTopScope(state.context) && stream.match(/^\s*\(/, false))))) style = "def"; if (hooks.token) { var result = hooks.token(stream, state, style); if (result !== undefined) style = result; } if (style == "def" && parserConfig.styleDefs === false) style = "variable"; state.startOfLine = false; state.prevToken = isDefKeyword ? "def" : style || curPunc; maybeEOL(stream, state); return style; }, indent: function(state, textAfter) { if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass; var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); var closing = firstChar == ctx.type; if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; if (parserConfig.dontIndentStatements) while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info)) ctx = ctx.prev if (hooks.indent) { var hook = hooks.indent(state, ctx, textAfter, indentUnit); if (typeof hook == "number") return hook } var switchBlock = ctx.prev && ctx.prev.info == "switch"; if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) { while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev return ctx.indented } if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1); if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit; return ctx.indented + (closing ? 0 : indentUnit) + (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0); }, electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/, blockCommentStart: "/*", blockCommentEnd: "*/", blockCommentContinue: " * ", lineComment: "//", fold: "brace" }; }); function words(str) { var obj = {}, words = str.split(" "); for (var i = 0; i < words.length; ++i) obj[words[i]] = true; return obj; } function contains(words, word) { if (typeof words === "function") { return words(word); } else { return words.propertyIsEnumerable(word); } } var cKeywords = "auto if break case register continue return default do sizeof " + "static else struct switch extern typedef union for goto while enum const " + "volatile inline restrict asm fortran"; // Do not use this. Use the cTypes function below. This is global just to avoid // excessive calls when cTypes is being called multiple times during a parse. var basicCTypes = words("int long char short double float unsigned signed " + "void bool"); // Do not use this. Use the objCTypes function below. This is global just to avoid // excessive calls when objCTypes is being called multiple times during a parse. var basicObjCTypes = words("SEL instancetype id Class Protocol BOOL"); // Returns true if identifier is a "C" type. // C type is defined as those that are reserved by the compiler (basicTypes), // and those that end in _t (Reserved by POSIX for types) // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html function cTypes(identifier) { return contains(basicCTypes, identifier) || /.+_t/.test(identifier); } // Returns true if identifier is a "Objective C" type. function objCTypes(identifier) { return cTypes(identifier) || contains(basicObjCTypes, identifier); } var cBlockKeywords = "case do else for if switch while struct enum union"; var cDefKeywords = "struct enum union"; function cppHook(stream, state) { if (!state.startOfLine) return false for (var ch, next = null; ch = stream.peek();) { if (ch == "\\" && stream.match(/^.$/)) { next = cppHook break } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) { break } stream.next() } state.tokenize = next return "meta" } function pointerHook(_stream, state) { if (state.prevToken == "type") return "type"; return false; } // For C and C++ (and ObjC): identifiers starting with __ // or _ followed by a capital letter are reserved for the compiler. function cIsReservedIdentifier(token) { if (!token || token.length < 2) return false; if (token[0] != '_') return false; return (token[1] == '_') || (token[1] !== token[1].toLowerCase()); } function cpp14Literal(stream) { stream.eatWhile(/[\w\.']/); return "number"; } function cpp11StringHook(stream, state) { stream.backUp(1); // Raw strings. if (stream.match(/(R|u8R|uR|UR|LR)/)) { var match = stream.match(/"([^\s\\()]{0,16})\(/); if (!match) { return false; } state.cpp11RawStringDelim = match[1]; state.tokenize = tokenRawString; return tokenRawString(stream, state); } // Unicode strings/chars. if (stream.match(/(u8|u|U|L)/)) { if (stream.match(/["']/, /* eat */ false)) { return "string"; } return false; } // Ignore this hook. stream.next(); return false; } function cppLooksLikeConstructor(word) { var lastTwo = /(\w+)::~?(\w+)$/.exec(word); return lastTwo && lastTwo[1] == lastTwo[2]; } // C#-style strings where "" escapes a quote. function tokenAtString(stream, state) { var next; while ((next = stream.next()) != null) { if (next == '"' && !stream.eat('"')) { state.tokenize = null; break; } } return "string"; } // C++11 raw string literal is "( anything )", where // can be a string up to 16 characters long. function tokenRawString(stream, state) { // Escape characters that have special regex meanings. var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&'); var match = stream.match(new RegExp(".*?\\)" + delim + '"')); if (match) state.tokenize = null; else stream.skipToEnd(); return "string"; } function def(mimes, mode) { if (typeof mimes == "string") mimes = [mimes]; var words = []; function add(obj) { if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop)) words.push(prop); } add(mode.keywords); add(mode.types); add(mode.builtin); add(mode.atoms); if (words.length) { mode.helperType = mimes[0]; CodeMirror.registerHelper("hintWords", mimes[0], words); } for (var i = 0; i < mimes.length; ++i) CodeMirror.defineMIME(mimes[i], mode); } def(["text/x-csrc", "text/x-c", "text/x-chdr"], { name: "clike", keywords: words(cKeywords), types: cTypes, blockKeywords: words(cBlockKeywords), defKeywords: words(cDefKeywords), typeFirstDefinitions: true, atoms: words("NULL true false"), isReservedIdentifier: cIsReservedIdentifier, hooks: { "#": cppHook, "*": pointerHook, }, modeProps: {fold: ["brace", "include"]} }); def(["text/x-c++src", "text/x-c++hdr"], { name: "clike", keywords: words(cKeywords + " dynamic_cast namespace reinterpret_cast try explicit new " + "static_cast typeid catch operator template typename class friend private " + "this using const_cast public throw virtual delete mutable protected " + "alignas alignof constexpr decltype nullptr noexcept thread_local final " + "static_assert override"), types: cTypes, blockKeywords: words(cBlockKeywords +" class try catch finally"), defKeywords: words(cDefKeywords + " class namespace"), typeFirstDefinitions: true, atoms: words("true false NULL"), dontIndentStatements: /^template$/, isIdentifierChar: /[\w\$_~\xa1-\uffff]/, isReservedIdentifier: cIsReservedIdentifier, hooks: { "#": cppHook, "*": pointerHook, "u": cpp11StringHook, "U": cpp11StringHook, "L": cpp11StringHook, "R": cpp11StringHook, "0": cpp14Literal, "1": cpp14Literal, "2": cpp14Literal, "3": cpp14Literal, "4": cpp14Literal, "5": cpp14Literal, "6": cpp14Literal, "7": cpp14Literal, "8": cpp14Literal, "9": cpp14Literal, token: function(stream, state, style) { if (style == "variable" && stream.peek() == "(" && (state.prevToken == ";" || state.prevToken == null || state.prevToken == "}") && cppLooksLikeConstructor(stream.current())) return "def"; } }, namespaceSeparator: "::", modeProps: {fold: ["brace", "include"]} }); def("text/x-java", { name: "clike", keywords: words("abstract assert break case catch class const continue default " + "do else enum extends final finally float for goto if implements import " + "instanceof interface native new package private protected public " + "return static strictfp super switch synchronized this throw throws transient " + "try volatile while @interface"), types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " + "Integer Long Number Object Short String StringBuffer StringBuilder Void"), blockKeywords: words("catch class do else finally for if switch try while"), defKeywords: words("class interface enum @interface"), typeFirstDefinitions: true, atoms: words("true false null"), number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, hooks: { "@": function(stream) { // Don't match the @interface keyword. if (stream.match('interface', false)) return false; stream.eatWhile(/[\w\$_]/); return "meta"; } }, modeProps: {fold: ["brace", "import"]} }); def("text/x-csharp", { name: "clike", keywords: words("abstract as async await base break case catch checked class const continue" + " default delegate do else enum event explicit extern finally fixed for" + " foreach goto if implicit in interface internal is lock namespace new" + " operator out override params private protected public readonly ref return sealed" + " sizeof stackalloc static struct switch this throw try typeof unchecked" + " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + " global group into join let orderby partial remove select set value var yield"), types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" + " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" + " UInt64 bool byte char decimal double short int long object" + " sbyte float string ushort uint ulong"), blockKeywords: words("catch class do else finally for foreach if struct switch try while"), defKeywords: words("class interface namespace struct var"), typeFirstDefinitions: true, atoms: words("true false null"), hooks: { "@": function(stream, state) { if (stream.eat('"')) { state.tokenize = tokenAtString; return tokenAtString(stream, state); } stream.eatWhile(/[\w\$_]/); return "meta"; } } }); function tokenTripleString(stream, state) { var escaped = false; while (!stream.eol()) { if (!escaped && stream.match('"""')) { state.tokenize = null; break; } escaped = stream.next() == "\\" && !escaped; } return "string"; } function tokenNestedComment(depth) { return function (stream, state) { var ch while (ch = stream.next()) { if (ch == "*" && stream.eat("/")) { if (depth == 1) { state.tokenize = null break } else { state.tokenize = tokenNestedComment(depth - 1) return state.tokenize(stream, state) } } else if (ch == "/" && stream.eat("*")) { state.tokenize = tokenNestedComment(depth + 1) return state.tokenize(stream, state) } } return "comment" } } def("text/x-scala", { name: "clike", keywords: words( /* scala */ "abstract case catch class def do else extends final finally for forSome if " + "implicit import lazy match new null object override package private protected return " + "sealed super this throw trait try type val var while with yield _ " + /* package scala */ "assert assume require print println printf readLine readBoolean readByte readShort " + "readChar readInt readLong readFloat readDouble" ), types: words( "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " + "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " + "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " + "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " + "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " + /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" ), multiLineStrings: true, blockKeywords: words("catch class enum do else finally for forSome if match switch try while"), defKeywords: words("class enum def object package trait type val var"), atoms: words("true false null"), indentStatements: false, indentSwitch: false, isOperatorChar: /[+\-*&%=<>!?|\/#:@]/, hooks: { "@": function(stream) { stream.eatWhile(/[\w\$_]/); return "meta"; }, '"': function(stream, state) { if (!stream.match('""')) return false; state.tokenize = tokenTripleString; return state.tokenize(stream, state); }, "'": function(stream) { stream.eatWhile(/[\w\$_\xa1-\uffff]/); return "atom"; }, "=": function(stream, state) { var cx = state.context if (cx.type == "}" && cx.align && stream.eat(">")) { state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev) return "operator" } else { return false } }, "/": function(stream, state) { if (!stream.eat("*")) return false state.tokenize = tokenNestedComment(1) return state.tokenize(stream, state) } }, modeProps: {closeBrackets: {pairs: '()[]{}""', triples: '"'}} }); function tokenKotlinString(tripleString){ return function (stream, state) { var escaped = false, next, end = false; while (!stream.eol()) { if (!tripleString && !escaped && stream.match('"') ) {end = true; break;} if (tripleString && stream.match('"""')) {end = true; break;} next = stream.next(); if(!escaped && next == "$" && stream.match('{')) stream.skipTo("}"); escaped = !escaped && next == "\\" && !tripleString; } if (end || !tripleString) state.tokenize = null; return "string"; } } def("text/x-kotlin", { name: "clike", keywords: words( /*keywords*/ "package as typealias class interface this super val operator " + "var fun for is in This throw return annotation " + "break continue object if else while do try when !in !is as? " + /*soft keywords*/ "file import where by get set abstract enum open inner override private public internal " + "protected catch finally out final vararg reified dynamic companion constructor init " + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + "external annotation crossinline const operator infix suspend actual expect setparam" ), types: words( /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " + "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " + "LazyThreadSafetyMode LongArray Nothing ShortArray Unit" ), intendSwitch: false, indentStatements: false, multiLineStrings: true, number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, blockKeywords: words("catch class do else finally for if where try while enum"), defKeywords: words("class val var object interface fun"), atoms: words("true false null this"), hooks: { "@": function(stream) { stream.eatWhile(/[\w\$_]/); return "meta"; }, '"': function(stream, state) { state.tokenize = tokenKotlinString(stream.match('""')); return state.tokenize(stream, state); }, indent: function(state, ctx, textAfter, indentUnit) { var firstChar = textAfter && textAfter.charAt(0); if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "") return state.indented; if (state.prevToken == "operator" && textAfter != "}" || state.prevToken == "variable" && firstChar == "." || (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".") return indentUnit * 2 + ctx.indented; if (ctx.align && ctx.type == "}") return ctx.indented + (state.context.type == (textAfter || "").charAt(0) ? 0 : indentUnit); } }, modeProps: {closeBrackets: {triples: '"'}} }); def(["x-shader/x-vertex", "x-shader/x-fragment"], { name: "clike", keywords: words("sampler1D sampler2D sampler3D samplerCube " + "sampler1DShadow sampler2DShadow " + "const attribute uniform varying " + "break continue discard return " + "for while do if else struct " + "in out inout"), types: words("float int bool void " + "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " + "mat2 mat3 mat4"), blockKeywords: words("for while do if else struct"), builtin: words("radians degrees sin cos tan asin acos atan " + "pow exp log exp2 sqrt inversesqrt " + "abs sign floor ceil fract mod min max clamp mix step smoothstep " + "length distance dot cross normalize ftransform faceforward " + "reflect refract matrixCompMult " + "lessThan lessThanEqual greaterThan greaterThanEqual " + "equal notEqual any all not " + "texture1D texture1DProj texture1DLod texture1DProjLod " + "texture2D texture2DProj texture2DLod texture2DProjLod " + "texture3D texture3DProj texture3DLod texture3DProjLod " + "textureCube textureCubeLod " + "shadow1D shadow2D shadow1DProj shadow2DProj " + "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " + "dFdx dFdy fwidth " + "noise1 noise2 noise3 noise4"), atoms: words("true false " + "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " + "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " + "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " + "gl_FogCoord gl_PointCoord " + "gl_Position gl_PointSize gl_ClipVertex " + "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " + "gl_TexCoord gl_FogFragCoord " + "gl_FragCoord gl_FrontFacing " + "gl_FragData gl_FragDepth " + "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " + "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " + "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " + "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " + "gl_ProjectionMatrixInverseTranspose " + "gl_ModelViewProjectionMatrixInverseTranspose " + "gl_TextureMatrixInverseTranspose " + "gl_NormalScale gl_DepthRange gl_ClipPlane " + "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " + "gl_FrontLightModelProduct gl_BackLightModelProduct " + "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " + "gl_FogParameters " + "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " + "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " + "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " + "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " + "gl_MaxDrawBuffers"), indentSwitch: false, hooks: {"#": cppHook}, modeProps: {fold: ["brace", "include"]} }); def("text/x-nesc", { name: "clike", keywords: words(cKeywords + " as atomic async call command component components configuration event generic " + "implementation includes interface module new norace nx_struct nx_union post provides " + "signal task uses abstract extends"), types: cTypes, blockKeywords: words(cBlockKeywords), atoms: words("null true false"), hooks: {"#": cppHook}, modeProps: {fold: ["brace", "include"]} }); def("text/x-objectivec", { name: "clike", keywords: words(cKeywords + " bycopy byref in inout oneway out self super atomic nonatomic retain copy " + "readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd " + "@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class " + "@public @package @private @protected @required @optional @try @catch @finally @import " + "@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available"), types: objCTypes, builtin: words("FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION NS_RETURNS_RETAINED " + "NS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER NS_DESIGNATED_INITIALIZER " + "NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION NS_ASSUME_NONNULL_BEGIN " + "NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT"), blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized"), defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class"), dontIndentStatements: /^@.*$/, typeFirstDefinitions: true, atoms: words("YES NO NULL Nil nil true false nullptr"), isReservedIdentifier: cIsReservedIdentifier, hooks: { "#": cppHook, "*": pointerHook, }, modeProps: {fold: ["brace", "include"]} }); def("text/x-squirrel", { name: "clike", keywords: words("base break clone continue const default delete enum extends function in class" + " foreach local resume return this throw typeof yield constructor instanceof static"), types: cTypes, blockKeywords: words("case catch class else for foreach if switch try while"), defKeywords: words("function local class"), typeFirstDefinitions: true, atoms: words("true false null"), hooks: {"#": cppHook}, modeProps: {fold: ["brace", "include"]} }); // Ceylon Strings need to deal with interpolation var stringTokenizer = null; function tokenCeylonString(type) { return function(stream, state) { var escaped = false, next, end = false; while (!stream.eol()) { if (!escaped && stream.match('"') && (type == "single" || stream.match('""'))) { end = true; break; } if (!escaped && stream.match('``')) { stringTokenizer = tokenCeylonString(type); end = true; break; } next = stream.next(); escaped = type == "single" && !escaped && next == "\\"; } if (end) state.tokenize = null; return "string"; } } def("text/x-ceylon", { name: "clike", keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" + " exists extends finally for function given if import in interface is let module new" + " nonempty object of out outer package return satisfies super switch then this throw" + " try value void while"), types: function(word) { // In Ceylon all identifiers that start with an uppercase are types var first = word.charAt(0); return (first === first.toUpperCase() && first !== first.toLowerCase()); }, blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"), defKeywords: words("class dynamic function interface module object package value"), builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" + " native optional sealed see serializable shared suppressWarnings tagged throws variable"), isPunctuationChar: /[\[\]{}\(\),;\:\.`]/, isOperatorChar: /[+\-*&%=<>!?|^~:\/]/, numberStart: /[\d#$]/, number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i, multiLineStrings: true, typeFirstDefinitions: true, atoms: words("true false null larger smaller equal empty finished"), indentSwitch: false, styleDefs: false, hooks: { "@": function(stream) { stream.eatWhile(/[\w\$_]/); return "meta"; }, '"': function(stream, state) { state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single"); return state.tokenize(stream, state); }, '`': function(stream, state) { if (!stringTokenizer || !stream.match('`')) return false; state.tokenize = stringTokenizer; stringTokenizer = null; return state.tokenize(stream, state); }, "'": function(stream) { stream.eatWhile(/[\w\$_\xa1-\uffff]/); return "atom"; }, token: function(_stream, state, style) { if ((style == "variable" || style == "type") && state.prevToken == ".") { return "variable-2"; } } }, modeProps: { fold: ["brace", "import"], closeBrackets: {triples: '"'} } }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/javascript/javascript.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var statementIndent = parserConfig.statementIndent; var jsonldMode = parserConfig.jsonld; var jsonMode = parserConfig.json || jsonldMode; var isTS = parserConfig.typescript; var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; // Tokenizer var keywords = function(){ function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; return { "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), "function": kw("function"), "catch": kw("catch"), "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, "this": kw("this"), "class": kw("class"), "super": kw("atom"), "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, "await": C }; }(); var isOperatorChar = /[+\-*&%=<>!?|~^@]/; var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; function readRegexp(stream) { var escaped = false, next, inSet = false; while ((next = stream.next()) != null) { if (!escaped) { if (next == "/" && !inSet) return; if (next == "[") inSet = true; else if (inSet && next == "]") inSet = false; } escaped = !escaped && next == "\\"; } } // Used as scratch variables to communicate multiple values without // consing up tons of objects. var type, content; function ret(tp, style, cont) { type = tp; content = cont; return style; } function tokenBase(stream, state) { var ch = stream.next(); if (ch == '"' || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { return ret("number", "number"); } else if (ch == "." && stream.match("..")) { return ret("spread", "meta"); } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { return ret(ch); } else if (ch == "=" && stream.eat(">")) { return ret("=>", "operator"); } else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) { return ret("number", "number"); } else if (/\d/.test(ch)) { stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/); return ret("number", "number"); } else if (ch == "/") { if (stream.eat("*")) { state.tokenize = tokenComment; return tokenComment(stream, state); } else if (stream.eat("/")) { stream.skipToEnd(); return ret("comment", "comment"); } else if (expressionAllowed(stream, state, 1)) { readRegexp(stream); stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/); return ret("regexp", "string-2"); } else { stream.eat("="); return ret("operator", "operator", stream.current()); } } else if (ch == "`") { state.tokenize = tokenQuasi; return tokenQuasi(stream, state); } else if (ch == "#") { stream.skipToEnd(); return ret("error", "error"); } else if (isOperatorChar.test(ch)) { if (ch != ">" || !state.lexical || state.lexical.type != ">") { if (stream.eat("=")) { if (ch == "!" || ch == "=") stream.eat("=") } else if (/[<>*+\-]/.test(ch)) { stream.eat(ch) if (ch == ">") stream.eat(ch) } } return ret("operator", "operator", stream.current()); } else if (wordRE.test(ch)) { stream.eatWhile(wordRE); var word = stream.current() if (state.lastType != ".") { if (keywords.propertyIsEnumerable(word)) { var kw = keywords[word] return ret(kw.type, kw.style, word) } if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false)) return ret("async", "keyword", word) } return ret("variable", "variable", word) } } function tokenString(quote) { return function(stream, state) { var escaped = false, next; if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ state.tokenize = tokenBase; return ret("jsonld-keyword", "meta"); } while ((next = stream.next()) != null) { if (next == quote && !escaped) break; escaped = !escaped && next == "\\"; } if (!escaped) state.tokenize = tokenBase; return ret("string", "string"); }; } function tokenComment(stream, state) { var maybeEnd = false, ch; while (ch = stream.next()) { if (ch == "/" && maybeEnd) { state.tokenize = tokenBase; break; } maybeEnd = (ch == "*"); } return ret("comment", "comment"); } function tokenQuasi(stream, state) { var escaped = false, next; while ((next = stream.next()) != null) { if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { state.tokenize = tokenBase; break; } escaped = !escaped && next == "\\"; } return ret("quasi", "string-2", stream.current()); } var brackets = "([{}])"; // This is a crude lookahead trick to try and notice that we're // parsing the argument patterns for a fat-arrow function before we // actually hit the arrow token. It only works if the arrow is on // the same line as the arguments and there's no strange noise // (comments) in between. Fallback is to only notice when we hit the // arrow, and not declare the arguments as locals for the arrow // body. function findFatArrow(stream, state) { if (state.fatArrowAt) state.fatArrowAt = null; var arrow = stream.string.indexOf("=>", stream.start); if (arrow < 0) return; if (isTS) { // Try to skip TypeScript return type declarations after the arguments var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) if (m) arrow = m.index } var depth = 0, sawSomething = false; for (var pos = arrow - 1; pos >= 0; --pos) { var ch = stream.string.charAt(pos); var bracket = brackets.indexOf(ch); if (bracket >= 0 && bracket < 3) { if (!depth) { ++pos; break; } if (--depth == 0) { if (ch == "(") sawSomething = true; break; } } else if (bracket >= 3 && bracket < 6) { ++depth; } else if (wordRE.test(ch)) { sawSomething = true; } else if (/["'\/]/.test(ch)) { return; } else if (sawSomething && !depth) { ++pos; break; } } if (sawSomething && !depth) state.fatArrowAt = pos; } // Parser var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; function JSLexical(indented, column, type, align, prev, info) { this.indented = indented; this.column = column; this.type = type; this.prev = prev; this.info = info; if (align != null) this.align = align; } function inScope(state, varname) { for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true; for (var cx = state.context; cx; cx = cx.prev) { for (var v = cx.vars; v; v = v.next) if (v.name == varname) return true; } } function parseJS(state, style, type, content, stream) { var cc = state.cc; // Communicate our context to the combinators. // (Less wasteful than consing up a hundred closures on every call.) cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; if (!state.lexical.hasOwnProperty("align")) state.lexical.align = true; while(true) { var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; if (combinator(type, content)) { while(cc.length && cc[cc.length - 1].lex) cc.pop()(); if (cx.marked) return cx.marked; if (type == "variable" && inScope(state, content)) return "variable-2"; return style; } } } // Combinator utils var cx = {state: null, column: null, marked: null, cc: null}; function pass() { for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); } function cont() { pass.apply(null, arguments); return true; } function inList(name, list) { for (var v = list; v; v = v.next) if (v.name == name) return true return false; } function register(varname) { var state = cx.state; cx.marked = "def"; if (state.context) { if (state.lexical.info == "var" && state.context && state.context.block) { // FIXME function decls are also not block scoped var newContext = registerVarScoped(varname, state.context) if (newContext != null) { state.context = newContext return } } else if (!inList(varname, state.localVars)) { state.localVars = new Var(varname, state.localVars) return } } // Fall through means this is global if (parserConfig.globalVars && !inList(varname, state.globalVars)) state.globalVars = new Var(varname, state.globalVars) } function registerVarScoped(varname, context) { if (!context) { return null } else if (context.block) { var inner = registerVarScoped(varname, context.prev) if (!inner) return null if (inner == context.prev) return context return new Context(inner, context.vars, true) } else if (inList(varname, context.vars)) { return context } else { return new Context(context.prev, new Var(varname, context.vars), false) } } function isModifier(name) { return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" } // Combinators function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block } function Var(name, next) { this.name = name; this.next = next } var defaultVars = new Var("this", new Var("arguments", null)) function pushcontext() { cx.state.context = new Context(cx.state.context, cx.state.localVars, false) cx.state.localVars = defaultVars } function pushblockcontext() { cx.state.context = new Context(cx.state.context, cx.state.localVars, true) cx.state.localVars = null } function popcontext() { cx.state.localVars = cx.state.context.vars cx.state.context = cx.state.context.prev } popcontext.lex = true function pushlex(type, info) { var result = function() { var state = cx.state, indent = state.indented; if (state.lexical.type == "stat") indent = state.lexical.indented; else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) indent = outer.indented; state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); }; result.lex = true; return result; } function poplex() { var state = cx.state; if (state.lexical.prev) { if (state.lexical.type == ")") state.indented = state.lexical.indented; state.lexical = state.lexical.prev; } } poplex.lex = true; function expect(wanted) { function exp(type) { if (type == wanted) return cont(); else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass(); else return cont(exp); }; return exp; } function statement(type, value) { if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex); if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); if (type == "keyword b") return cont(pushlex("form"), statement, poplex); if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex); if (type == "debugger") return cont(expect(";")); if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext); if (type == ";") return cont(); if (type == "if") { if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) cx.state.cc.pop()(); return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); } if (type == "function") return cont(functiondef); if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); } if (type == "variable") { if (isTS && value == "declare") { cx.marked = "keyword" return cont(statement) } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { cx.marked = "keyword" if (value == "enum") return cont(enumdef); else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";")); else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) } else if (isTS && value == "namespace") { cx.marked = "keyword" return cont(pushlex("form"), expression, block, poplex) } else if (isTS && value == "abstract") { cx.marked = "keyword" return cont(statement) } else { return cont(pushlex("stat"), maybelabel); } } if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext, block, poplex, poplex, popcontext); if (type == "case") return cont(expression, expect(":")); if (type == "default") return cont(expect(":")); if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext); if (type == "export") return cont(pushlex("stat"), afterExport, poplex); if (type == "import") return cont(pushlex("stat"), afterImport, poplex); if (type == "async") return cont(statement) if (value == "@") return cont(expression, statement) return pass(pushlex("stat"), expression, expect(";"), poplex); } function maybeCatchBinding(type) { if (type == "(") return cont(funarg, expect(")")) } function expression(type, value) { return expressionInner(type, value, false); } function expressionNoComma(type, value) { return expressionInner(type, value, true); } function parenExpr(type) { if (type != "(") return pass() return cont(pushlex(")"), expression, expect(")"), poplex) } function expressionInner(type, value, noComma) { if (cx.state.fatArrowAt == cx.stream.start) { var body = noComma ? arrowBodyNoComma : arrowBody; if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); } var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); if (type == "function") return cont(functiondef, maybeop); if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); if (type == "{") return contCommasep(objprop, "}", null, maybeop); if (type == "quasi") return pass(quasi, maybeop); if (type == "new") return cont(maybeTarget(noComma)); if (type == "import") return cont(expression); return cont(); } function maybeexpression(type) { if (type.match(/[;\}\)\],]/)) return pass(); return pass(expression); } function maybeoperatorComma(type, value) { if (type == ",") return cont(expression); return maybeoperatorNoComma(type, value, false); } function maybeoperatorNoComma(type, value, noComma) { var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; var expr = noComma == false ? expression : expressionNoComma; if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "operator") { if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false)) return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); if (value == "?") return cont(expression, expect(":"), expr); return cont(expr); } if (type == "quasi") { return pass(quasi, me); } if (type == ";") return; if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); if (type == ".") return cont(property, me); if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } if (type == "regexp") { cx.state.lastType = cx.marked = "operator" cx.stream.backUp(cx.stream.pos - cx.stream.start - 1) return cont(expr) } } function quasi(type, value) { if (type != "quasi") return pass(); if (value.slice(value.length - 2) != "${") return cont(quasi); return cont(expression, continueQuasi); } function continueQuasi(type) { if (type == "}") { cx.marked = "string-2"; cx.state.tokenize = tokenQuasi; return cont(quasi); } } function arrowBody(type) { findFatArrow(cx.stream, cx.state); return pass(type == "{" ? statement : expression); } function arrowBodyNoComma(type) { findFatArrow(cx.stream, cx.state); return pass(type == "{" ? statement : expressionNoComma); } function maybeTarget(noComma) { return function(type) { if (type == ".") return cont(noComma ? targetNoComma : target); else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) else return pass(noComma ? expressionNoComma : expression); }; } function target(_, value) { if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } } function targetNoComma(_, value) { if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } } function maybelabel(type) { if (type == ":") return cont(poplex, statement); return pass(maybeoperatorComma, expect(";"), poplex); } function property(type) { if (type == "variable") {cx.marked = "property"; return cont();} } function objprop(type, value) { if (type == "async") { cx.marked = "property"; return cont(objprop); } else if (type == "variable" || cx.style == "keyword") { cx.marked = "property"; if (value == "get" || value == "set") return cont(getterSetter); var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false))) cx.state.fatArrowAt = cx.stream.pos + m[0].length return cont(afterprop); } else if (type == "number" || type == "string") { cx.marked = jsonldMode ? "property" : (cx.style + " property"); return cont(afterprop); } else if (type == "jsonld-keyword") { return cont(afterprop); } else if (isTS && isModifier(value)) { cx.marked = "keyword" return cont(objprop) } else if (type == "[") { return cont(expression, maybetype, expect("]"), afterprop); } else if (type == "spread") { return cont(expressionNoComma, afterprop); } else if (value == "*") { cx.marked = "keyword"; return cont(objprop); } else if (type == ":") { return pass(afterprop) } } function getterSetter(type) { if (type != "variable") return pass(afterprop); cx.marked = "property"; return cont(functiondef); } function afterprop(type) { if (type == ":") return cont(expressionNoComma); if (type == "(") return pass(functiondef); } function commasep(what, end, sep) { function proceed(type, value) { if (sep ? sep.indexOf(type) > -1 : type == ",") { var lex = cx.state.lexical; if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; return cont(function(type, value) { if (type == end || value == end) return pass() return pass(what) }, proceed); } if (type == end || value == end) return cont(); return cont(expect(end)); } return function(type, value) { if (type == end || value == end) return cont(); return pass(what, proceed); }; } function contCommasep(what, end, info) { for (var i = 3; i < arguments.length; i++) cx.cc.push(arguments[i]); return cont(pushlex(end, info), commasep(what, end), poplex); } function block(type) { if (type == "}") return cont(); return pass(statement, block); } function maybetype(type, value) { if (isTS) { if (type == ":") return cont(typeexpr); if (value == "?") return cont(maybetype); } } function mayberettype(type) { if (isTS && type == ":") { if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) else return cont(typeexpr) } } function isKW(_, value) { if (value == "is") { cx.marked = "keyword" return cont() } } function typeexpr(type, value) { if (value == "keyof" || value == "typeof") { cx.marked = "keyword" return cont(value == "keyof" ? typeexpr : expressionNoComma) } if (type == "variable" || value == "void") { cx.marked = "type" return cont(afterType) } if (type == "string" || type == "number" || type == "atom") return cont(afterType); if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) } function maybeReturnType(type) { if (type == "=>") return cont(typeexpr) } function typeprop(type, value) { if (type == "variable" || cx.style == "keyword") { cx.marked = "property" return cont(typeprop) } else if (value == "?") { return cont(typeprop) } else if (type == ":") { return cont(typeexpr) } else if (type == "[") { return cont(expression, maybetype, expect("]"), typeprop) } } function typearg(type, value) { if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) if (type == ":") return cont(typeexpr) return pass(typeexpr) } function afterType(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) if (value == "|" || type == "." || value == "&") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } } function maybeTypeArgs(_, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) } function typeparam() { return pass(typeexpr, maybeTypeDefault) } function maybeTypeDefault(_, value) { if (value == "=") return cont(typeexpr) } function vardef(_, value) { if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} return pass(pattern, maybetype, maybeAssign, vardefCont); } function pattern(type, value) { if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } if (type == "variable") { register(value); return cont(); } if (type == "spread") return cont(pattern); if (type == "[") return contCommasep(eltpattern, "]"); if (type == "{") return contCommasep(proppattern, "}"); } function proppattern(type, value) { if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { register(value); return cont(maybeAssign); } if (type == "variable") cx.marked = "property"; if (type == "spread") return cont(pattern); if (type == "}") return pass(); return cont(expect(":"), pattern, maybeAssign); } function eltpattern() { return pass(pattern, maybeAssign) } function maybeAssign(_type, value) { if (value == "=") return cont(expressionNoComma); } function vardefCont(type) { if (type == ",") return cont(vardef); } function maybeelse(type, value) { if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); } function forspec(type, value) { if (value == "await") return cont(forspec); if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); } function forspec1(type) { if (type == "var") return cont(vardef, expect(";"), forspec2); if (type == ";") return cont(forspec2); if (type == "variable") return cont(formaybeinof); return pass(expression, expect(";"), forspec2); } function formaybeinof(_type, value) { if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } return cont(maybeoperatorComma, forspec2); } function forspec2(type, value) { if (type == ";") return cont(forspec3); if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } return pass(expression, expect(";"), forspec3); } function forspec3(type) { if (type != ")") cont(expression); } function functiondef(type, value) { if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} if (type == "variable") {register(value); return cont(functiondef);} if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) } function funarg(type, value) { if (value == "@") cont(expression, funarg) if (type == "spread") return cont(funarg); if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } return pass(pattern, maybetype, maybeAssign); } function classExpression(type, value) { // Class expressions may have an optional name. if (type == "variable") return className(type, value); return classNameAfter(type, value); } function className(type, value) { if (type == "variable") {register(value); return cont(classNameAfter);} } function classNameAfter(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) if (value == "extends" || value == "implements" || (isTS && type == ",")) { if (value == "implements") cx.marked = "keyword"; return cont(isTS ? typeexpr : expression, classNameAfter); } if (type == "{") return cont(pushlex("}"), classBody, poplex); } function classBody(type, value) { if (type == "async" || (type == "variable" && (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) { cx.marked = "keyword"; return cont(classBody); } if (type == "variable" || cx.style == "keyword") { cx.marked = "property"; return cont(isTS ? classfield : functiondef, classBody); } if (type == "[") return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody) if (value == "*") { cx.marked = "keyword"; return cont(classBody); } if (type == ";") return cont(classBody); if (type == "}") return cont(); if (value == "@") return cont(expression, classBody) } function classfield(type, value) { if (value == "?") return cont(classfield) if (type == ":") return cont(typeexpr, maybeAssign) if (value == "=") return cont(expressionNoComma) return pass(functiondef) } function afterExport(type, value) { if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); return pass(statement); } function exportField(type, value) { if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } if (type == "variable") return pass(expressionNoComma, exportField); } function afterImport(type) { if (type == "string") return cont(); if (type == "(") return pass(expression); return pass(importSpec, maybeMoreImports, maybeFrom); } function importSpec(type, value) { if (type == "{") return contCommasep(importSpec, "}"); if (type == "variable") register(value); if (value == "*") cx.marked = "keyword"; return cont(maybeAs); } function maybeMoreImports(type) { if (type == ",") return cont(importSpec, maybeMoreImports) } function maybeAs(_type, value) { if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } } function maybeFrom(_type, value) { if (value == "from") { cx.marked = "keyword"; return cont(expression); } } function arrayLiteral(type) { if (type == "]") return cont(); return pass(commasep(expressionNoComma, "]")); } function enumdef() { return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) } function enummember() { return pass(pattern, maybeAssign); } function isContinuedStatement(state, textAfter) { return state.lastType == "operator" || state.lastType == "," || isOperatorChar.test(textAfter.charAt(0)) || /[,.]/.test(textAfter.charAt(0)); } function expressionAllowed(stream, state, backUp) { return state.tokenize == tokenBase && /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) || (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) } // Interface return { startState: function(basecolumn) { var state = { tokenize: tokenBase, lastType: "sof", cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, context: parserConfig.localVars && new Context(null, null, false), indented: basecolumn || 0 }; if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") state.globalVars = parserConfig.globalVars; return state; }, token: function(stream, state) { if (stream.sol()) { if (!state.lexical.hasOwnProperty("align")) state.lexical.align = false; state.indented = stream.indentation(); findFatArrow(stream, state); } if (state.tokenize != tokenComment && stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; return parseJS(state, style, type, content, stream); }, indent: function(state, textAfter) { if (state.tokenize == tokenComment) return CodeMirror.Pass; if (state.tokenize != tokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top // Kludge to prevent 'maybelse' from blocking lexical scope pops if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { var c = state.cc[i]; if (c == poplex) lexical = lexical.prev; else if (c != maybeelse) break; } while ((lexical.type == "stat" || lexical.type == "form") && (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && (top == maybeoperatorComma || top == maybeoperatorNoComma) && !/^[,\.=+\-*:?[\(]/.test(textAfter)))) lexical = lexical.prev; if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; else if (type == "form") return lexical.indented + indentUnit; else if (type == "stat") return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); else if (lexical.align) return lexical.column + (closing ? 0 : 1); else return lexical.indented + (closing ? 0 : indentUnit); }, electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, blockCommentStart: jsonMode ? null : "/*", blockCommentEnd: jsonMode ? null : "*/", blockCommentContinue: jsonMode ? null : " * ", lineComment: jsonMode ? null : "//", fold: "brace", closeBrackets: "()[]{}''\"\"``", helperType: jsonMode ? "json" : "javascript", jsonldMode: jsonldMode, jsonMode: jsonMode, expressionAllowed: expressionAllowed, skipExpression: function(state) { var top = state.cc[state.cc.length - 1] if (top == expression || top == expressionNoComma) state.cc.pop() } }; }); CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); CodeMirror.defineMIME("text/javascript", "javascript"); CodeMirror.defineMIME("text/ecmascript", "javascript"); CodeMirror.defineMIME("application/javascript", "javascript"); CodeMirror.defineMIME("application/x-javascript", "javascript"); CodeMirror.defineMIME("application/ecmascript", "javascript"); CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/php/php.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; function keywords(str) { var obj = {}, words = str.split(" "); for (var i = 0; i < words.length; ++i) obj[words[i]] = true; return obj; } // Helper for phpString function matchSequence(list, end, escapes) { if (list.length == 0) return phpString(end); return function (stream, state) { var patterns = list[0]; for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) { state.tokenize = matchSequence(list.slice(1), end); return patterns[i][1]; } state.tokenize = phpString(end, escapes); return "string"; }; } function phpString(closing, escapes) { return function(stream, state) { return phpString_(stream, state, closing, escapes); }; } function phpString_(stream, state, closing, escapes) { // "Complex" syntax if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) { state.tokenize = null; return "string"; } // Simple syntax if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) { // After the variable name there may appear array or object operator. if (stream.match("[", false)) { // Match array operator state.tokenize = matchSequence([ [["[", null]], [[/\d[\w\.]*/, "number"], [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"], [/[\w\$]+/, "variable"]], [["]", null]] ], closing, escapes); } if (stream.match(/\-\>\w/, false)) { // Match object operator state.tokenize = matchSequence([ [["->", null]], [[/[\w]+/, "variable"]] ], closing, escapes); } return "variable-2"; } var escaped = false; // Normal string while (!stream.eol() && (escaped || escapes === false || (!stream.match("{$", false) && !stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) { if (!escaped && stream.match(closing)) { state.tokenize = null; state.tokStack.pop(); state.tokStack.pop(); break; } escaped = stream.next() == "\\" && !escaped; } return "string"; } var phpKeywords = "abstract and array as break case catch class clone const continue declare default " + "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " + "for foreach function global goto if implements interface instanceof namespace " + "new or private protected public static switch throw trait try use var while xor " + "die echo empty exit eval include include_once isset list require require_once return " + "print unset __halt_compiler self static parent yield insteadof finally"; var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__"; var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count"; CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" ")); CodeMirror.registerHelper("wordChars", "php", /[\w$]/); var phpConfig = { name: "clike", helperType: "php", keywords: keywords(phpKeywords), blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"), defKeywords: keywords("class function interface namespace trait"), atoms: keywords(phpAtoms), builtin: keywords(phpBuiltin), multiLineStrings: true, hooks: { "$": function(stream) { stream.eatWhile(/[\w\$_]/); return "variable-2"; }, "<": function(stream, state) { var before; if (before = stream.match(/<<\s*/)) { var quoted = stream.eat(/['"]/); stream.eatWhile(/[\w\.]/); var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1)); if (quoted) stream.eat(quoted); if (delim) { (state.tokStack || (state.tokStack = [])).push(delim, 0); state.tokenize = phpString(delim, quoted != "'"); return "string"; } } return false; }, "#": function(stream) { while (!stream.eol() && !stream.match("?>", false)) stream.next(); return "comment"; }, "/": function(stream) { if (stream.eat("/")) { while (!stream.eol() && !stream.match("?>", false)) stream.next(); return "comment"; } return false; }, '"': function(_stream, state) { (state.tokStack || (state.tokStack = [])).push('"', 0); state.tokenize = phpString('"'); return "string"; }, "{": function(_stream, state) { if (state.tokStack && state.tokStack.length) state.tokStack[state.tokStack.length - 1]++; return false; }, "}": function(_stream, state) { if (state.tokStack && state.tokStack.length > 0 && !--state.tokStack[state.tokStack.length - 1]) { state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]); } return false; } } }; CodeMirror.defineMode("php", function(config, parserConfig) { var htmlMode = CodeMirror.getMode(config, (parserConfig && parserConfig.htmlMode) || "text/html"); var phpMode = CodeMirror.getMode(config, phpConfig); function dispatch(stream, state) { var isPHP = state.curMode == phpMode; if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null; if (!isPHP) { if (stream.match(/^<\?\w*/)) { state.curMode = phpMode; if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, "")) state.curState = state.php; return "meta"; } if (state.pending == '"' || state.pending == "'") { while (!stream.eol() && stream.next() != state.pending) {} var style = "string"; } else if (state.pending && stream.pos < state.pending.end) { stream.pos = state.pending.end; var style = state.pending.style; } else { var style = htmlMode.token(stream, state.curState); } if (state.pending) state.pending = null; var cur = stream.current(), openPHP = cur.search(/<\?/), m; if (openPHP != -1) { if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0]; else state.pending = {end: stream.pos, style: style}; stream.backUp(cur.length - openPHP); } return style; } else if (isPHP && state.php.tokenize == null && stream.match("?>")) { state.curMode = htmlMode; state.curState = state.html; if (!state.php.context.prev) state.php = null; return "meta"; } else { return phpMode.token(stream, state.curState); } } return { startState: function() { var html = CodeMirror.startState(htmlMode) var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null return {html: html, php: php, curMode: parserConfig.startOpen ? phpMode : htmlMode, curState: parserConfig.startOpen ? php : html, pending: null}; }, copyState: function(state) { var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html), php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur; if (state.curMode == htmlMode) cur = htmlNew; else cur = phpNew; return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur, pending: state.pending}; }, token: dispatch, indent: function(state, textAfter) { if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) || (state.curMode == phpMode && /^\?>/.test(textAfter))) return htmlMode.indent(state.html, textAfter); return state.curMode.indent(state.curState, textAfter); }, blockCommentStart: "/*", blockCommentEnd: "*/", lineComment: "//", innerMode: function(state) { return {state: state.curState, mode: state.curMode}; } }; }, "htmlmixed", "clike"); CodeMirror.defineMIME("application/x-httpd-php", "php"); CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true}); CodeMirror.defineMIME("text/x-php", phpConfig); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/powershell/powershell.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { 'use strict'; if (typeof exports == 'object' && typeof module == 'object') // CommonJS mod(require('../../lib/codemirror')); else if (typeof define == 'function' && define.amd) // AMD define(['../../lib/codemirror'], mod); else // Plain browser env mod(window.CodeMirror); })(function(CodeMirror) { 'use strict'; CodeMirror.defineMode('powershell', function() { function buildRegexp(patterns, options) { options = options || {}; var prefix = options.prefix !== undefined ? options.prefix : '^'; var suffix = options.suffix !== undefined ? options.suffix : '\\b'; for (var i = 0; i < patterns.length; i++) { if (patterns[i] instanceof RegExp) { patterns[i] = patterns[i].source; } else { patterns[i] = patterns[i].replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } } return new RegExp(prefix + '(' + patterns.join('|') + ')' + suffix, 'i'); } var notCharacterOrDash = '(?=[^A-Za-z\\d\\-_]|$)'; var varNames = /[\w\-:]/ var keywords = buildRegexp([ /begin|break|catch|continue|data|default|do|dynamicparam/, /else|elseif|end|exit|filter|finally|for|foreach|from|function|if|in/, /param|process|return|switch|throw|trap|try|until|where|while/ ], { suffix: notCharacterOrDash }); var punctuation = /[\[\]{},;`\.]|@[({]/; var wordOperators = buildRegexp([ 'f', /b?not/, /[ic]?split/, 'join', /is(not)?/, 'as', /[ic]?(eq|ne|[gl][te])/, /[ic]?(not)?(like|match|contains)/, /[ic]?replace/, /b?(and|or|xor)/ ], { prefix: '-' }); var symbolOperators = /[+\-*\/%]=|\+\+|--|\.\.|[+\-*&^%:=!|\/]|<(?!#)|(?!#)>/; var operators = buildRegexp([wordOperators, symbolOperators], { suffix: '' }); var numbers = /^((0x[\da-f]+)|((\d+\.\d+|\d\.|\.\d+|\d+)(e[\+\-]?\d+)?))[ld]?([kmgtp]b)?/i; var identifiers = /^[A-Za-z\_][A-Za-z\-\_\d]*\b/; var symbolBuiltins = /[A-Z]:|%|\?/i; var namedBuiltins = buildRegexp([ /Add-(Computer|Content|History|Member|PSSnapin|Type)/, /Checkpoint-Computer/, /Clear-(Content|EventLog|History|Host|Item(Property)?|Variable)/, /Compare-Object/, /Complete-Transaction/, /Connect-PSSession/, /ConvertFrom-(Csv|Json|SecureString|StringData)/, /Convert-Path/, /ConvertTo-(Csv|Html|Json|SecureString|Xml)/, /Copy-Item(Property)?/, /Debug-Process/, /Disable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/, /Disconnect-PSSession/, /Enable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/, /(Enter|Exit)-PSSession/, /Export-(Alias|Clixml|Console|Counter|Csv|FormatData|ModuleMember|PSSession)/, /ForEach-Object/, /Format-(Custom|List|Table|Wide)/, new RegExp('Get-(Acl|Alias|AuthenticodeSignature|ChildItem|Command|ComputerRestorePoint|Content|ControlPanelItem|Counter|Credential' + '|Culture|Date|Event|EventLog|EventSubscriber|ExecutionPolicy|FormatData|Help|History|Host|HotFix|Item|ItemProperty|Job' + '|Location|Member|Module|PfxCertificate|Process|PSBreakpoint|PSCallStack|PSDrive|PSProvider|PSSession|PSSessionConfiguration' + '|PSSnapin|Random|Service|TraceSource|Transaction|TypeData|UICulture|Unique|Variable|Verb|WinEvent|WmiObject)'), /Group-Object/, /Import-(Alias|Clixml|Counter|Csv|LocalizedData|Module|PSSession)/, /ImportSystemModules/, /Invoke-(Command|Expression|History|Item|RestMethod|WebRequest|WmiMethod)/, /Join-Path/, /Limit-EventLog/, /Measure-(Command|Object)/, /Move-Item(Property)?/, new RegExp('New-(Alias|Event|EventLog|Item(Property)?|Module|ModuleManifest|Object|PSDrive|PSSession|PSSessionConfigurationFile' + '|PSSessionOption|PSTransportOption|Service|TimeSpan|Variable|WebServiceProxy|WinEvent)'), /Out-(Default|File|GridView|Host|Null|Printer|String)/, /Pause/, /(Pop|Push)-Location/, /Read-Host/, /Receive-(Job|PSSession)/, /Register-(EngineEvent|ObjectEvent|PSSessionConfiguration|WmiEvent)/, /Remove-(Computer|Event|EventLog|Item(Property)?|Job|Module|PSBreakpoint|PSDrive|PSSession|PSSnapin|TypeData|Variable|WmiObject)/, /Rename-(Computer|Item(Property)?)/, /Reset-ComputerMachinePassword/, /Resolve-Path/, /Restart-(Computer|Service)/, /Restore-Computer/, /Resume-(Job|Service)/, /Save-Help/, /Select-(Object|String|Xml)/, /Send-MailMessage/, new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' + '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'), /Show-(Command|ControlPanelItem|EventLog)/, /Sort-Object/, /Split-Path/, /Start-(Job|Process|Service|Sleep|Transaction|Transcript)/, /Stop-(Computer|Job|Process|Service|Transcript)/, /Suspend-(Job|Service)/, /TabExpansion2/, /Tee-Object/, /Test-(ComputerSecureChannel|Connection|ModuleManifest|Path|PSSessionConfigurationFile)/, /Trace-Command/, /Unblock-File/, /Undo-Transaction/, /Unregister-(Event|PSSessionConfiguration)/, /Update-(FormatData|Help|List|TypeData)/, /Use-Transaction/, /Wait-(Event|Job|Process)/, /Where-Object/, /Write-(Debug|Error|EventLog|Host|Output|Progress|Verbose|Warning)/, /cd|help|mkdir|more|oss|prompt/, /ac|asnp|cat|cd|chdir|clc|clear|clhy|cli|clp|cls|clv|cnsn|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|dnsn|ebp/, /echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fl|foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|gl|gm|gmo|gp|gps/, /group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md/, /measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri/, /rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls/, /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/ ], { prefix: '', suffix: '' }); var variableBuiltins = buildRegexp([ /[$?^_]|Args|ConfirmPreference|ConsoleFileName|DebugPreference|Error|ErrorActionPreference|ErrorView|ExecutionContext/, /FormatEnumerationLimit|Home|Host|Input|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount/, /MaximumHistoryCount|MaximumVariableCount|MyInvocation|NestedPromptLevel|OutputEncoding|Pid|Profile|ProgressPreference/, /PSBoundParameters|PSCommandPath|PSCulture|PSDefaultParameterValues|PSEmailServer|PSHome|PSScriptRoot|PSSessionApplicationName/, /PSSessionConfigurationName|PSSessionOption|PSUICulture|PSVersionTable|Pwd|ShellId|StackTrace|VerbosePreference/, /WarningPreference|WhatIfPreference/, /Event|EventArgs|EventSubscriber|Sender/, /Matches|Ofs|ForEach|LastExitCode|PSCmdlet|PSItem|PSSenderInfo|This/, /true|false|null/ ], { prefix: '\\$', suffix: '' }); var builtins = buildRegexp([symbolBuiltins, namedBuiltins, variableBuiltins], { suffix: notCharacterOrDash }); var grammar = { keyword: keywords, number: numbers, operator: operators, builtin: builtins, punctuation: punctuation, identifier: identifiers }; // tokenizers function tokenBase(stream, state) { // Handle Comments //var ch = stream.peek(); var parent = state.returnStack[state.returnStack.length - 1]; if (parent && parent.shouldReturnFrom(state)) { state.tokenize = parent.tokenize; state.returnStack.pop(); return state.tokenize(stream, state); } if (stream.eatSpace()) { return null; } if (stream.eat('(')) { state.bracketNesting += 1; return 'punctuation'; } if (stream.eat(')')) { state.bracketNesting -= 1; return 'punctuation'; } for (var key in grammar) { if (stream.match(grammar[key])) { return key; } } var ch = stream.next(); // single-quote string if (ch === "'") { return tokenSingleQuoteString(stream, state); } if (ch === '$') { return tokenVariable(stream, state); } // double-quote string if (ch === '"') { return tokenDoubleQuoteString(stream, state); } if (ch === '<' && stream.eat('#')) { state.tokenize = tokenComment; return tokenComment(stream, state); } if (ch === '#') { stream.skipToEnd(); return 'comment'; } if (ch === '@') { var quoteMatch = stream.eat(/["']/); if (quoteMatch && stream.eol()) { state.tokenize = tokenMultiString; state.startQuote = quoteMatch[0]; return tokenMultiString(stream, state); } else if (stream.eol()) { return 'error'; } else if (stream.peek().match(/[({]/)) { return 'punctuation'; } else if (stream.peek().match(varNames)) { // splatted variable return tokenVariable(stream, state); } } return 'error'; } function tokenSingleQuoteString(stream, state) { var ch; while ((ch = stream.peek()) != null) { stream.next(); if (ch === "'" && !stream.eat("'")) { state.tokenize = tokenBase; return 'string'; } } return 'error'; } function tokenDoubleQuoteString(stream, state) { var ch; while ((ch = stream.peek()) != null) { if (ch === '$') { state.tokenize = tokenStringInterpolation; return 'string'; } stream.next(); if (ch === '`') { stream.next(); continue; } if (ch === '"' && !stream.eat('"')) { state.tokenize = tokenBase; return 'string'; } } return 'error'; } function tokenStringInterpolation(stream, state) { return tokenInterpolation(stream, state, tokenDoubleQuoteString); } function tokenMultiStringReturn(stream, state) { state.tokenize = tokenMultiString; state.startQuote = '"' return tokenMultiString(stream, state); } function tokenHereStringInterpolation(stream, state) { return tokenInterpolation(stream, state, tokenMultiStringReturn); } function tokenInterpolation(stream, state, parentTokenize) { if (stream.match('$(')) { var savedBracketNesting = state.bracketNesting; state.returnStack.push({ /*jshint loopfunc:true */ shouldReturnFrom: function(state) { return state.bracketNesting === savedBracketNesting; }, tokenize: parentTokenize }); state.tokenize = tokenBase; state.bracketNesting += 1; return 'punctuation'; } else { stream.next(); state.returnStack.push({ shouldReturnFrom: function() { return true; }, tokenize: parentTokenize }); state.tokenize = tokenVariable; return state.tokenize(stream, state); } } function tokenComment(stream, state) { var maybeEnd = false, ch; while ((ch = stream.next()) != null) { if (maybeEnd && ch == '>') { state.tokenize = tokenBase; break; } maybeEnd = (ch === '#'); } return 'comment'; } function tokenVariable(stream, state) { var ch = stream.peek(); if (stream.eat('{')) { state.tokenize = tokenVariableWithBraces; return tokenVariableWithBraces(stream, state); } else if (ch != undefined && ch.match(varNames)) { stream.eatWhile(varNames); state.tokenize = tokenBase; return 'variable-2'; } else { state.tokenize = tokenBase; return 'error'; } } function tokenVariableWithBraces(stream, state) { var ch; while ((ch = stream.next()) != null) { if (ch === '}') { state.tokenize = tokenBase; break; } } return 'variable-2'; } function tokenMultiString(stream, state) { var quote = state.startQuote; if (stream.sol() && stream.match(new RegExp(quote + '@'))) { state.tokenize = tokenBase; } else if (quote === '"') { while (!stream.eol()) { var ch = stream.peek(); if (ch === '$') { state.tokenize = tokenHereStringInterpolation; return 'string'; } stream.next(); if (ch === '`') { stream.next(); } } } else { stream.skipToEnd(); } return 'string'; } var external = { startState: function() { return { returnStack: [], bracketNesting: 0, tokenize: tokenBase }; }, token: function(stream, state) { return state.tokenize(stream, state); }, blockCommentStart: '<#', blockCommentEnd: '#>', lineComment: '#', fold: 'brace' }; return external; }); CodeMirror.defineMIME('application/x-powershell', 'powershell'); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/python/python.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b"); } var wordOperators = wordRegexp(["and", "or", "not", "is"]); var commonKeywords = ["as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "lambda", "pass", "raise", "return", "try", "while", "with", "yield", "in"]; var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__", "NotImplemented", "Ellipsis", "__debug__"]; CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins)); function top(state) { return state.scopes[state.scopes.length - 1]; } CodeMirror.defineMode("python", function(conf, parserConf) { var ERRORCLASS = "error"; var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/; // (Backwards-compatiblity with old, cumbersome config system) var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters, parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@])/] for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1) var hangingIndent = parserConf.hangingIndent || conf.indentUnit; var myKeywords = commonKeywords, myBuiltins = commonBuiltins; if (parserConf.extra_keywords != undefined) myKeywords = myKeywords.concat(parserConf.extra_keywords); if (parserConf.extra_builtins != undefined) myBuiltins = myBuiltins.concat(parserConf.extra_builtins); var py3 = !(parserConf.version && Number(parserConf.version) < 3) if (py3) { // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]); myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]); var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i"); } else { var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; myKeywords = myKeywords.concat(["exec", "print"]); myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile", "file", "intern", "long", "raw_input", "reduce", "reload", "unichr", "unicode", "xrange", "False", "True", "None"]); var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); } var keywords = wordRegexp(myKeywords); var builtins = wordRegexp(myBuiltins); // tokenizers function tokenBase(stream, state) { var sol = stream.sol() && state.lastToken != "\\" if (sol) state.indent = stream.indentation() // Handle scope changes if (sol && top(state).type == "py") { var scopeOffset = top(state).offset; if (stream.eatSpace()) { var lineOffset = stream.indentation(); if (lineOffset > scopeOffset) pushPyScope(state); else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#") state.errorToken = true; return null; } else { var style = tokenBaseInner(stream, state); if (scopeOffset > 0 && dedent(stream, state)) style += " " + ERRORCLASS; return style; } } return tokenBaseInner(stream, state); } function tokenBaseInner(stream, state) { if (stream.eatSpace()) return null; // Handle Comments if (stream.match(/^#.*/)) return "comment"; // Handle Number Literals if (stream.match(/^[0-9\.]/, false)) { var floatLiteral = false; // Floats if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; } if (stream.match(/^\.\d+/)) { floatLiteral = true; } if (floatLiteral) { // Float literals may be "imaginary" stream.eat(/J/i); return "number"; } // Integers var intLiteral = false; // Hex if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true; // Binary if (stream.match(/^0b[01_]+/i)) intLiteral = true; // Octal if (stream.match(/^0o[0-7_]+/i)) intLiteral = true; // Decimal if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) { // Decimal literals may be "imaginary" stream.eat(/J/i); // TODO - Can you have imaginary longs? intLiteral = true; } // Zero by itself with no other piece of number. if (stream.match(/^0(?![\dx])/i)) intLiteral = true; if (intLiteral) { // Integer literals may be "long" stream.eat(/L/i); return "number"; } } // Handle Strings if (stream.match(stringPrefixes)) { var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1; if (!isFmtString) { state.tokenize = tokenStringFactory(stream.current()); return state.tokenize(stream, state); } else { state.tokenize = formatStringFactory(stream.current(), state.tokenize); return state.tokenize(stream, state); } } for (var i = 0; i < operators.length; i++) if (stream.match(operators[i])) return "operator" if (stream.match(delimiters)) return "punctuation"; if (state.lastToken == "." && stream.match(identifiers)) return "property"; if (stream.match(keywords) || stream.match(wordOperators)) return "keyword"; if (stream.match(builtins)) return "builtin"; if (stream.match(/^(self|cls)\b/)) return "variable-2"; if (stream.match(identifiers)) { if (state.lastToken == "def" || state.lastToken == "class") return "def"; return "variable"; } // Handle non-detected items stream.next(); return ERRORCLASS; } function formatStringFactory(delimiter, tokenOuter) { while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) delimiter = delimiter.substr(1); var singleline = delimiter.length == 1; var OUTCLASS = "string"; function tokenFString(stream, state) { // inside f-str Expression if (stream.match(delimiter)) { // expression ends pre-maturally, but very common in editing // Could show error to remind users to close brace here state.tokenize = tokenString return OUTCLASS; } else if (stream.match('{')) { // starting brace, if not eaten below return "punctuation"; } else if (stream.match('}')) { // return to regular inside string state state.tokenize = tokenString return "punctuation"; } else { // use tokenBaseInner to parse the expression return tokenBaseInner(stream, state); } } function tokenString(stream, state) { while (!stream.eol()) { stream.eatWhile(/[^'"\{\}\\]/); if (stream.eat("\\")) { stream.next(); if (singleline && stream.eol()) return OUTCLASS; } else if (stream.match(delimiter)) { state.tokenize = tokenOuter; return OUTCLASS; } else if (stream.match('{{')) { // ignore {{ in f-str return OUTCLASS; } else if (stream.match('{', false)) { // switch to nested mode state.tokenize = tokenFString if (stream.current()) { return OUTCLASS; } else { // need to return something, so eat the starting { stream.next(); return "punctuation"; } } else if (stream.match('}}')) { return OUTCLASS; } else if (stream.match('}')) { // single } in f-string is an error return ERRORCLASS; } else { stream.eat(/['"]/); } } if (singleline) { if (parserConf.singleLineStringErrors) return ERRORCLASS; else state.tokenize = tokenOuter; } return OUTCLASS; } tokenString.isString = true; return tokenString; } function tokenStringFactory(delimiter) { while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) delimiter = delimiter.substr(1); var singleline = delimiter.length == 1; var OUTCLASS = "string"; function tokenString(stream, state) { while (!stream.eol()) { stream.eatWhile(/[^'"\\]/); if (stream.eat("\\")) { stream.next(); if (singleline && stream.eol()) return OUTCLASS; } else if (stream.match(delimiter)) { state.tokenize = tokenBase; return OUTCLASS; } else { stream.eat(/['"]/); } } if (singleline) { if (parserConf.singleLineStringErrors) return ERRORCLASS; else state.tokenize = tokenBase; } return OUTCLASS; } tokenString.isString = true; return tokenString; } function pushPyScope(state) { while (top(state).type != "py") state.scopes.pop() state.scopes.push({offset: top(state).offset + conf.indentUnit, type: "py", align: null}) } function pushBracketScope(stream, state, type) { var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1 state.scopes.push({offset: state.indent + hangingIndent, type: type, align: align}) } function dedent(stream, state) { var indented = stream.indentation(); while (state.scopes.length > 1 && top(state).offset > indented) { if (top(state).type != "py") return true; state.scopes.pop(); } return top(state).offset != indented; } function tokenLexer(stream, state) { if (stream.sol()) state.beginningOfLine = true; var style = state.tokenize(stream, state); var current = stream.current(); // Handle decorators if (state.beginningOfLine && current == "@") return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS; if (/\S/.test(current)) state.beginningOfLine = false; if ((style == "variable" || style == "builtin") && state.lastToken == "meta") style = "meta"; // Handle scope changes. if (current == "pass" || current == "return") state.dedent += 1; if (current == "lambda") state.lambda = true; if (current == ":" && !state.lambda && top(state).type == "py") pushPyScope(state); if (current.length == 1 && !/string|comment/.test(style)) { var delimiter_index = "[({".indexOf(current); if (delimiter_index != -1) pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); delimiter_index = "])}".indexOf(current); if (delimiter_index != -1) { if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent else return ERRORCLASS; } } if (state.dedent > 0 && stream.eol() && top(state).type == "py") { if (state.scopes.length > 1) state.scopes.pop(); state.dedent -= 1; } return style; } var external = { startState: function(basecolumn) { return { tokenize: tokenBase, scopes: [{offset: basecolumn || 0, type: "py", align: null}], indent: basecolumn || 0, lastToken: null, lambda: false, dedent: 0 }; }, token: function(stream, state) { var addErr = state.errorToken; if (addErr) state.errorToken = false; var style = tokenLexer(stream, state); if (style && style != "comment") state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style; if (style == "punctuation") style = null; if (stream.eol() && state.lambda) state.lambda = false; return addErr ? style + " " + ERRORCLASS : style; }, indent: function(state, textAfter) { if (state.tokenize != tokenBase) return state.tokenize.isString ? CodeMirror.Pass : 0; var scope = top(state), closing = scope.type == textAfter.charAt(0) if (scope.align != null) return scope.align - (closing ? 1 : 0) else return scope.offset - (closing ? hangingIndent : 0) }, electricInput: /^\s*[\}\]\)]$/, closeBrackets: {triples: "'\""}, lineComment: "#", fold: "indent" }; return external; }); CodeMirror.defineMIME("text/x-python", "python"); var words = function(str) { return str.split(" "); }; CodeMirror.defineMIME("text/x-cython", { name: "python", extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+ "extern gil include nogil property public "+ "readonly struct union DEF IF ELIF ELSE") }); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/codemirror/mode/shell/shell.js ================================================ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineMode('shell', function() { var words = {}; function define(style, dict) { for(var i = 0; i < dict.length; i++) { words[dict[i]] = style; } }; var commonAtoms = ["true", "false"]; var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi", "fin", "fil", "done", "exit", "set", "unset", "export", "function"]; var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear", "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall", "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm", "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop", "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write", "yes", "zsh"]; CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands)); define('atom', commonAtoms); define('keyword', commonKeywords); define('builtin', commonCommands); function tokenBase(stream, state) { if (stream.eatSpace()) return null; var sol = stream.sol(); var ch = stream.next(); if (ch === '\\') { stream.next(); return null; } if (ch === '\'' || ch === '"' || ch === '`') { state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string")); return tokenize(stream, state); } if (ch === '#') { if (sol && stream.eat('!')) { stream.skipToEnd(); return 'meta'; // 'comment'? } stream.skipToEnd(); return 'comment'; } if (ch === '$') { state.tokens.unshift(tokenDollar); return tokenize(stream, state); } if (ch === '+' || ch === '=') { return 'operator'; } if (ch === '-') { stream.eat('-'); stream.eatWhile(/\w/); return 'attribute'; } if (/\d/.test(ch)) { stream.eatWhile(/\d/); if(stream.eol() || !/\w/.test(stream.peek())) { return 'number'; } } stream.eatWhile(/[\w-]/); var cur = stream.current(); if (stream.peek() === '=' && /\w+/.test(cur)) return 'def'; return words.hasOwnProperty(cur) ? words[cur] : null; } function tokenString(quote, style) { var close = quote == "(" ? ")" : quote == "{" ? "}" : quote return function(stream, state) { var next, escaped = false; while ((next = stream.next()) != null) { if (next === close && !escaped) { state.tokens.shift(); break; } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) { escaped = true; stream.backUp(1); state.tokens.unshift(tokenDollar); break; } else if (!escaped && quote !== close && next === quote) { state.tokens.unshift(tokenString(quote, style)) return tokenize(stream, state) } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) { state.tokens.unshift(tokenStringStart(next, "string")); stream.backUp(1); break; } escaped = !escaped && next === '\\'; } return style; }; }; function tokenStringStart(quote, style) { return function(stream, state) { state.tokens[0] = tokenString(quote, style) stream.next() return tokenize(stream, state) } } var tokenDollar = function(stream, state) { if (state.tokens.length > 1) stream.eat('$'); var ch = stream.next() if (/['"({]/.test(ch)) { state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string"); return tokenize(stream, state); } if (!/\d/.test(ch)) stream.eatWhile(/\w/); state.tokens.shift(); return 'def'; }; function tokenize(stream, state) { return (state.tokens[0] || tokenBase) (stream, state); }; return { startState: function() {return {tokens:[]};}, token: function(stream, state) { return tokenize(stream, state); }, closeBrackets: "()[]{}''\"\"``", lineComment: '#', fold: "brace" }; }); CodeMirror.defineMIME('text/x-sh', 'shell'); // Apache uses a slightly different Media Type for Shell scripts // http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types CodeMirror.defineMIME('application/x-sh', 'shell'); }); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/cronGen/cronGen.js ================================================ (function ($) { // var resultsName = ""; var inputElement; var displayElement; $.fn.extend({ cronGen: function (options) { if (options == null) { options = {}; } options = $.extend({}, $.fn.cronGen.defaultOptions, options); //create top menu var cronContainer = $("
    ", { id: "CronContainer", style: "display:none;width:300px;height:300px;" }); var mainDiv = $("
    ", { id: "CronGenMainDiv", style: "width:410px;height:420px;" }); var topMenu = $("
      ", { "class": "nav nav-tabs", id: "CronGenTabs" }); $('
    • ', { 'class': 'active' }).html($('')).appendTo(topMenu); $('
    • ').html($('分钟')).appendTo(topMenu); $('
    • ').html($('小时')).appendTo(topMenu); $('
    • ').html($('')).appendTo(topMenu); $('
    • ').html($('')).appendTo(topMenu); $('
    • ').html($('')).appendTo(topMenu); $('
    • ').html($('')).appendTo(topMenu); $(topMenu).appendTo(mainDiv); //create what's inside the tabs var container = $("
      ", { "class": "container-fluid", "style": "margin-top: 30px;margin-left: -14px;" }); var row = $("
      ", { "class": "row-fluid" }); var span12 = $("
      ", { "class": "span12" }); var tabContent = $("
      ", { "class": "tab-content", "style": "border:0px; margin-top:-20px;" }); //creating the secondsTab var secondsTab = $("
      ", { "class": "tab-pane active", id: "Secondly" }); var seconds1 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "1", name : "second"}).appendTo(seconds1); $(seconds1).append("每秒 允许的通配符[, - * /]"); $(seconds1).appendTo(secondsTab); var seconds2 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "2", name : "second"}).appendTo(seconds2); $(seconds2).append("周期 从"); $("",{type : "text", id : "secondStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds2); $(seconds2).append("-"); $("",{type : "text", id : "secondEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds2); $(seconds2).append("秒"); $(seconds2).appendTo(secondsTab); var seconds3 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "3", name : "second"}).appendTo(seconds3); $(seconds3).append("从"); $("",{type : "text", id : "secondStart_1", value : "0", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds3); $(seconds3).append("秒开始,每"); $("",{type : "text", id : "secondEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds3); $(seconds3).append("秒执行一次"); $(seconds3).appendTo(secondsTab); var seconds4 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "4", name : "second", id: "sencond_appoint"}).appendTo(seconds4); $(seconds4).append("指定"); $(seconds4).appendTo(secondsTab); $(secondsTab).append('
      00010203040506070809
      '); $(secondsTab).append('
      10111213141516171819
      '); $(secondsTab).append('
      20212223242526272829
      '); $(secondsTab).append('
      30313233343536373839
      '); $(secondsTab).append('
      40414243444546474849
      '); $(secondsTab).append('
      50515253545556575859
      '); $("",{type : "hidden", id : "secondHidden"}).appendTo(secondsTab); $(secondsTab).appendTo(tabContent); //creating the minutesTab var minutesTab = $("
      ", { "class": "tab-pane", id: "Minutes" }); var minutes1 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "1", name : "min"}).appendTo(minutes1); $(minutes1).append("每分钟 允许的通配符[, - * /]"); $(minutes1).appendTo(minutesTab); var minutes2 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "2", name : "min"}).appendTo(minutes2); $(minutes2).append("周期 从"); $("",{type : "text", id : "minStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes2); $(minutes2).append("-"); $("",{type : "text", id : "minEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes2); $(minutes2).append("分钟"); $(minutes2).appendTo(minutesTab); var minutes3 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "3", name : "min"}).appendTo(minutes3); $(minutes3).append("从"); $("",{type : "text", id : "minStart_1", value : "0", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes3); $(minutes3).append("分钟开始,每"); $("",{type : "text", id : "minEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes3); $(minutes3).append("分钟执行一次"); $(minutes3).appendTo(minutesTab); var minutes4 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "4", name : "min", id: "min_appoint"}).appendTo(minutes4); $(minutes4).append("指定"); $(minutes4).appendTo(minutesTab); $(minutesTab).append('
      00010203040506070809
      '); $(minutesTab).append('
      10111213141516171819
      '); $(minutesTab).append('
      20212223242526272829
      '); $(minutesTab).append('
      30313233343536373839
      '); $(minutesTab).append('
      40414243444546474849
      '); $(minutesTab).append('
      50515253545556575859
      '); $("",{type : "hidden", id : "minHidden"}).appendTo(minutesTab); $(minutesTab).appendTo(tabContent); //creating the hourlyTab var hourlyTab = $("
      ", { "class": "tab-pane", id: "Hourly" }); var hourly1 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "1", name : "hour"}).appendTo(hourly1); $(hourly1).append("每小时 允许的通配符[, - * /]"); $(hourly1).appendTo(hourlyTab); var hourly2 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "2", name : "hour"}).appendTo(hourly2); $(hourly2).append("周期 从"); $("",{type : "text", id : "hourStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly2); $(hourly2).append("-"); $("",{type : "text", id : "hourEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly2); $(hourly2).append("小时"); $(hourly2).appendTo(hourlyTab); var hourly3 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "3", name : "hour"}).appendTo(hourly3); $(hourly3).append("从"); $("",{type : "text", id : "hourStart_1", value : "0", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly3); $(hourly3).append("小时开始,每"); $("",{type : "text", id : "hourEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly3); $(hourly3).append("小时执行一次"); $(hourly3).appendTo(hourlyTab); var hourly4 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "4", name : "hour", id: "hour_appoint"}).appendTo(hourly4); $(hourly4).append("指定"); $(hourly4).appendTo(hourlyTab); $(hourlyTab).append('
      000102030405
      '); $(hourlyTab).append('
      060708091011
      '); $(hourlyTab).append('
      121314151617
      '); $(hourlyTab).append('
      181920212223
      '); $("",{type : "hidden", id : "hourHidden"}).appendTo(hourlyTab); $(hourlyTab).appendTo(tabContent); //creating the dailyTab var dailyTab = $("
      ", { "class": "tab-pane", id: "Daily" }); var daily1 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "1", name : "day"}).appendTo(daily1); $(daily1).append("每天 允许的通配符[, - * / L W]"); $(daily1).appendTo(dailyTab); var daily5 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "2", name : "day"}).appendTo(daily5); $(daily5).append("不指定"); $(daily5).appendTo(dailyTab); var daily2 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "3", name : "day"}).appendTo(daily2); $(daily2).append("周期 从"); $("",{type : "text", id : "dayStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily2); $(daily2).append("-"); $("",{type : "text", id : "dayEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily2); $(daily2).append("日"); $(daily2).appendTo(dailyTab); var daily3 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "4", name : "day"}).appendTo(daily3); $(daily3).append("从"); $("",{type : "text", id : "dayStart_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily3); $(daily3).append("日开始,每"); $("",{type : "text", id : "dayEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily3); $(daily3).append("天执行一次"); $(daily3).appendTo(dailyTab); var daily6 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "5", name : "day"}).appendTo(daily6); $(daily6).append("每月"); $("",{type : "text", id : "dayStart_2", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily6); $(daily6).append("号最近的那个工作日"); $(daily6).appendTo(dailyTab); var daily7 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "6", name : "day"}).appendTo(daily7); $(daily7).append("本月最后一天"); $(daily7).appendTo(dailyTab); var daily4 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "7", name : "day", id: "day_appoint"}).appendTo(daily4); $(daily4).append("指定"); $(daily4).appendTo(dailyTab); $(dailyTab).append('
      01020304050607080910
      '); $(dailyTab).append('
      11121314151617181920
      '); $(dailyTab).append('
      21222324252627282930
      '); $(dailyTab).append('
      31
      '); $("",{type : "hidden", id : "dayHidden"}).appendTo(dailyTab); $(dailyTab).appendTo(tabContent); //creating the monthlyTab var monthlyTab = $("
      ", { "class": "tab-pane", id: "Monthly" }); var monthly1 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "1", name : "month"}).appendTo(monthly1); $(monthly1).append("每月 允许的通配符[, - * /]"); $(monthly1).appendTo(monthlyTab); var monthly2 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "2", name : "month"}).appendTo(monthly2); $(monthly2).append("不指定"); $(monthly2).appendTo(monthlyTab); var monthly3 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "3", name : "month"}).appendTo(monthly3); $(monthly3).append("周期 从"); $("",{type : "text", id : "monthStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly3); $(monthly3).append("-"); $("",{type : "text", id : "monthEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly3); $(monthly3).append("月"); $(monthly3).appendTo(monthlyTab); var monthly4 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "4", name : "month"}).appendTo(monthly4); $(monthly4).append("从"); $("",{type : "text", id : "monthStart_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly4); $(monthly4).append("月开始,每"); $("",{type : "text", id : "monthEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly4); $(monthly4).append("月执行一次"); $(monthly4).appendTo(monthlyTab); var monthly5 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "5", name : "month", id: "month_appoint"}).appendTo(monthly5); $(monthly5).append("指定"); $(monthly5).appendTo(monthlyTab); $(monthlyTab).append('
      010203040506
      '); $(monthlyTab).append('
      070809101112
      '); $("",{type : "hidden", id : "monthHidden"}).appendTo(monthlyTab); $(monthlyTab).appendTo(tabContent); //creating the weeklyTab var weeklyTab = $("
      ", { "class": "tab-pane", id: "Weekly" }); var weekly1 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "1", name : "week"}).appendTo(weekly1); $(weekly1).append("每周 允许的通配符[, - * / L #]"); $(weekly1).appendTo(weeklyTab); var weekly2 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "2", name : "week"}).appendTo(weekly2); $(weekly2).append("不指定"); $(weekly2).appendTo(weeklyTab); var weekly3 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "3", name : "week"}).appendTo(weekly3); $(weekly3).append("周期 从星期"); $("",{type : "text", id : "weekStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly3); $(weekly3).append("-"); $("",{type : "text", id : "weekEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly3); $(weekly3).appendTo(weeklyTab); var weekly4 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "4", name : "week"}).appendTo(weekly4); $(weekly4).append("第"); $("",{type : "text", id : "weekStart_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly4); $(weekly4).append("周的星期"); $("",{type : "text", id : "weekEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly4); $(weekly4).appendTo(weeklyTab); var weekly5 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "5", name : "week"}).appendTo(weekly5); $(weekly5).append("本月最后一个星期"); $("",{type : "text", id : "weekStart_2", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly5); $(weekly5).appendTo(weeklyTab); var weekly6 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "6", name : "week", id: "week_appoint"}).appendTo(weekly6); $(weekly6).append("指定"); $(weekly6).appendTo(weeklyTab); $(weeklyTab).append('
      1234567
      '); $("",{type : "hidden", id : "weekHidden"}).appendTo(weeklyTab); $(weeklyTab).appendTo(tabContent); //creating the yearlyTab var yearlyTab = $("
      ", { "class": "tab-pane", id: "Yearly" }); var yearly1 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "1", name : "year"}).appendTo(yearly1); $(yearly1).append("不指定 允许的通配符[, - * /] 非必填"); $(yearly1).appendTo(yearlyTab); var yearly3 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "2", name : "year"}).appendTo(yearly3); $(yearly3).append("每年"); $(yearly3).appendTo(yearlyTab); var yearly2 = $("
      ",{"class":"line"}); $("",{type : "radio", value : "3", name : "year"}).appendTo(yearly2); $(yearly2).append("周期从"); $("",{type : "text", id : "yearStart_0", value : "2016", style:"width:45px; height:20px;"}).appendTo(yearly2); $(yearly2).append("-"); $("",{type : "text", id : "yearEnd_0", value : "2017", style:"width:45px; height:20px;"}).appendTo(yearly2); $(yearly2).append("年"); $(yearly2).appendTo(yearlyTab); $("",{type : "hidden", id : "yearHidden"}).appendTo(yearlyTab); $(yearlyTab).appendTo(tabContent); $(tabContent).appendTo(span12); //creating the button and results input // resultsName = $(this).prop("id"); // $(this).prop("name", resultsName); var runTime = '

      '; $(span12).appendTo(row); $(row).appendTo(container); $(container).appendTo(mainDiv); $(runTime).appendTo(mainDiv); $(cronContainer).append(mainDiv); var that = $(this); // Hide the original input that.hide(); // Replace the input with an input group var $g = $("
      ").addClass("input-group"); // Add an input var $i = $("", { type: 'text', placeholder: 'cron表达式...', name: 'cronGen_display' }).addClass("form-control").val($(that).val()); $i.appendTo($g); // Add the button var $b = $(""); // Put button inside span var $s = $("").addClass("input-group-btn"); $b.appendTo($s); $s.appendTo($g); $(this).before($g); inputElement = that; displayElement = $i; $b.popover({ html: true, content: function () { return $(cronContainer).html(); }, template: '

      ', sanitize:false, placement: options.direction }).on('click', function (e) { if (inputElement.val().trim() !== '') { refreshRunTime(); } e.preventDefault(); //fillDataOfMinutesAndHoursSelectOptions(); //fillDayWeekInMonth(); //fillInWeekDays(); //fillInMonths(); $.fn.cronGen.tools.cronParse(inputElement.val()); //绑定指定事件 $.fn.cronGen.tools.initChangeEvent(); $('#CronGenTabs a').click(function (e) { e.preventDefault(); $(this).tab('show'); //generate(); }); $("#CronGenMainDiv select,input").change(function (e) { generate(); refreshRunTime(); }); $("#CronGenMainDiv input").focus(function (e) { generate(); }); //generate(); }); return; } }); var fillInMonths = function () { var days = [ { text: "一月", val: "1" }, { text: "二月", val: "2" }, { text: "三月", val: "3" }, { text: "四月", val: "4" }, { text: "五月", val: "5" }, { text: "六月", val: "6" }, { text: "七月", val: "7" }, { text: "八月", val: "8" }, { text: "九月", val: "9" }, { text: "十月", val: "10" }, { text: "十一月", val: "11" }, { text: "十二月", val: "12" } ]; $(".months").each(function () { fillOptions(this, days); }); }; var fillOptions = function (elements, options) { for (var i = 0; i < options.length; i++) $(elements).append(""); }; var fillDataOfMinutesAndHoursSelectOptions = function () { for (var i = 0; i < 60; i++) { if (i < 24) { $(".hours").each(function () { $(this).append(timeSelectOption(i)); }); } $(".minutes").each(function () { $(this).append(timeSelectOption(i)); }); } }; var fillInWeekDays = function () { var days = [ { text: "周一", val: "2" }, { text: "周二", val: "3" }, { text: "周三", val: "4" }, { text: "周四", val: "5" }, { text: "周五", val: "6" }, { text: "周六", val: "7" }, { text: "周天", val: "1" } ]; $(".week-days").each(function () { fillOptions(this, days); }); }; var fillDayWeekInMonth = function () { var days = [ { text: "第一个", val: "1" }, { text: "第二个", val: "2" }, { text: "第三个", val: "3" }, { text: "第四个", val: "4" } ]; $(".day-order-in-month").each(function () { fillOptions(this, days); }); }; var displayTimeUnit = function (unit) { if (unit.toString().length == 1) return "0" + unit; return unit; }; var timeSelectOption = function (i) { return ""; }; var generate = function () { var activeTab = $("ul#CronGenTabs li.active a").prop("id"); if (activeTab == undefined) { return; } var results = ""; switch (activeTab) { case "SecondlyTab": switch ($("input:radio[name=second]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("second"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.cycle("second"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.startOn("second"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.initCheckBox("second"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "MinutesTab": switch ($("input:radio[name=min]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("min"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.cycle("min"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.startOn("min"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.initCheckBox("min"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "HourlyTab": switch ($("input:radio[name=hour]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("hour"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.cycle("hour"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.startOn("hour"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.initCheckBox("hour"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "DailyTab": switch ($("input:radio[name=day]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("day"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.unAppoint("day"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("day"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.startOn("day"); results = $.fn.cronGen.tools.cronResult(); break; case "5": $.fn.cronGen.tools.workDay("day"); results = $.fn.cronGen.tools.cronResult(); break; case "6": $.fn.cronGen.tools.lastDay("day"); results = $.fn.cronGen.tools.cronResult(); break; case "7": $.fn.cronGen.tools.initCheckBox("day"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "WeeklyTab": switch ($("input:radio[name=week]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("week"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.unAppoint("week"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("week"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.startOn("week"); results = $.fn.cronGen.tools.cronResult(); break; case "5": $.fn.cronGen.tools.lastWeek("week"); results = $.fn.cronGen.tools.cronResult(); break; case "6": $.fn.cronGen.tools.initCheckBox("week"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "MonthlyTab": switch ($("input:radio[name=month]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("month"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.unAppoint("month"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("month"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.startOn("month"); results = $.fn.cronGen.tools.cronResult(); break; case "5": $.fn.cronGen.tools.initCheckBox("month"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "YearlyTab": switch ($("input:radio[name=year]:checked").val()) { case "1": $.fn.cronGen.tools.unAppoint("year"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.everyTime("year"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("year"); results = $.fn.cronGen.tools.cronResult(); break; } break; } // Update original control inputElement.val(results); // Update display displayElement.val(results); }; var refreshRunTime = function () { $.ajax({ type : 'GET', url : base_url + "/jobinfo/nextTriggerTime", data : { "scheduleType" : 'CRON', "scheduleConf" : inputElement.val() }, dataType : "json", success : function(data){ if (data.code === 200) { $('#runTime').val(data.content.join("\n")); } else { $('#runTime').val(data.msg); } } }); }; })(jQuery); (function($) { $.fn.cronGen.defaultOptions = { direction : 'bottom' }; $.fn.cronGen.tools = { /** * 每周期 */ everyTime : function(dom){ $("#"+dom+"Hidden").val("*"); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 不指定 */ unAppoint : function(dom){ var val = "?"; if (dom == "year") { val = ""; } $("#"+dom+"Hidden").val(val); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 周期 */ cycle : function(dom){ var start = $("#"+dom+"Start_0").val(); var end = $("#"+dom+"End_0").val(); $("#"+dom+"Hidden").val(start + "-" + end); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 从开始 */ startOn : function(dom) { var start = $("#"+dom+"Start_1").val(); var end = $("#"+dom+"End_1").val(); $("#"+dom+"Hidden").val(start + "/" + end); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 最后一天 */ lastDay : function(dom){ $("#"+dom+"Hidden").val("L"); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 每周的某一天 */ weekOfDay : function(dom){ var start = $("#"+dom+"Start_0").val(); var end = $("#"+dom+"End_0").val(); $("#"+dom+"Hidden").val(start + "#" + end); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 最后一周 */ lastWeek : function(dom){ var start = $("#"+dom+"Start_2").val(); $("#"+dom+"Hidden").val(start+"L"); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 工作日 */ workDay : function(dom) { var start = $("#"+dom+"Start_2").val(); $("#"+dom+"Hidden").val(start + "W"); $.fn.cronGen.tools.clearCheckbox(dom); }, initChangeEvent : function(){ var secondList = $(".secondList").children(); $("#sencond_appoint").click(function(){ if (this.checked) { if ($(secondList).filter(":checked").length == 0) { $(secondList.eq(0)).attr("checked", true); } secondList.eq(0).change(); } }); secondList.change(function() { var sencond_appoint = $("#sencond_appoint").prop("checked"); if (sencond_appoint) { var vals = []; secondList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 59) { val = vals.join(","); }else if(vals.length == 59){ val = "*"; } $("#secondHidden").val(val); } }); var minList = $(".minList").children(); $("#min_appoint").click(function(){ if (this.checked) { if ($(minList).filter(":checked").length == 0) { $(minList.eq(0)).attr("checked", true); } minList.eq(0).change(); } }); minList.change(function() { var min_appoint = $("#min_appoint").prop("checked"); if (min_appoint) { var vals = []; minList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 59) { val = vals.join(","); }else if(vals.length == 59){ val = "*"; } $("#minHidden").val(val); } }); var hourList = $(".hourList").children(); $("#hour_appoint").click(function(){ if (this.checked) { if ($(hourList).filter(":checked").length == 0) { $(hourList.eq(0)).attr("checked", true); } hourList.eq(0).change(); } }); hourList.change(function() { var hour_appoint = $("#hour_appoint").prop("checked"); if (hour_appoint) { var vals = []; hourList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 24) { val = vals.join(","); }else if(vals.length == 24){ val = "*"; } $("#hourHidden").val(val); } }); var dayList = $(".dayList").children(); $("#day_appoint").click(function(){ if (this.checked) { if ($(dayList).filter(":checked").length == 0) { $(dayList.eq(0)).attr("checked", true); } dayList.eq(0).change(); } }); dayList.change(function() { var day_appoint = $("#day_appoint").prop("checked"); if (day_appoint) { var vals = []; dayList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 31) { val = vals.join(","); }else if(vals.length == 31){ val = "*"; } $("#dayHidden").val(val); } }); var monthList = $(".monthList").children(); $("#month_appoint").click(function(){ if (this.checked) { if ($(monthList).filter(":checked").length == 0) { $(monthList.eq(0)).attr("checked", true); } monthList.eq(0).change(); } }); monthList.change(function() { var month_appoint = $("#month_appoint").prop("checked"); if (month_appoint) { var vals = []; monthList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 12) { val = vals.join(","); }else if(vals.length == 12){ val = "*"; } $("#monthHidden").val(val); } }); var weekList = $(".weekList").children(); $("#week_appoint").click(function(){ if (this.checked) { if ($(weekList).filter(":checked").length == 0) { $(weekList.eq(0)).attr("checked", true); } weekList.eq(0).change(); } }); weekList.change(function() { var week_appoint = $("#week_appoint").prop("checked"); if (week_appoint) { var vals = []; weekList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 7) { val = vals.join(","); }else if(vals.length == 7){ val = "*"; } $("#weekHidden").val(val); } }); }, initObj : function(strVal, strid){ var ary = null; var objRadio = $("input[name='" + strid + "'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(1).attr("checked", "checked"); $("#" + strid + "Start_0").val(ary[0]); $("#" + strid + "End_0").val(ary[1]); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(2).attr("checked", "checked"); $("#" + strid + "Start_1").val(ary[0]); $("#" + strid + "End_1").val(ary[1]); } else { objRadio.eq(3).attr("checked", "checked"); if (strVal != "?") { ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $("." + strid + "List input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox(strid); } } }, initDay : function(strVal) { var ary = null; var objRadio = $("input[name='day'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal == "?") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(2).attr("checked", "checked"); $("#dayStart_0").val(ary[0]); $("#dayEnd_0").val(ary[1]); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(3).attr("checked", "checked"); $("#dayStart_1").val(ary[0]); $("#dayEnd_1").val(ary[1]); } else if (strVal.split('W').length > 1) { ary = strVal.split('W'); objRadio.eq(4).attr("checked", "checked"); $("#dayStart_2").val(ary[0]); } else if (strVal == "L") { objRadio.eq(5).attr("checked", "checked"); } else { objRadio.eq(6).attr("checked", "checked"); ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $(".dayList input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox("day"); } }, initMonth : function(strVal) { var ary = null; var objRadio = $("input[name='month'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal == "?") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(2).attr("checked", "checked"); $("#monthStart_0").val(ary[0]); $("#monthEnd_0").val(ary[1]); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(3).attr("checked", "checked"); $("#monthStart_1").val(ary[0]); $("#monthEnd_1").val(ary[1]); } else { objRadio.eq(4).attr("checked", "checked"); ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $(".monthList input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox("month"); } }, initWeek : function(strVal) { var ary = null; var objRadio = $("input[name='week'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal == "?") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(2).attr("checked", "checked"); $("#weekStart_0").val(ary[0]); $("#weekEnd_0").val(ary[1]); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(3).attr("checked", "checked"); $("#weekStart_1").val(ary[0]); $("#weekEnd_1").val(ary[1]); } else if (strVal.split('L').length > 1) { ary = strVal.split('L'); objRadio.eq(4).attr("checked", "checked"); $("#weekStart_2").val(ary[0]); } else { objRadio.eq(5).attr("checked", "checked"); ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $(".weekList input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox("week"); } }, initYear : function(strVal) { var ary = null; var objRadio = $("input[name='year'"); if (strVal == "*") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(2).attr("checked", "checked"); $("#yearStart_0").val(ary[0]); $("#yearEnd_0").val(ary[1]); } }, cronParse : function(cronExpress) { //获取参数中表达式的值 if (cronExpress) { var regs = cronExpress.split(' '); $("#secondHidden").val(regs[0]); $("#minHidden").val(regs[1]); $("#hourHidden").val(regs[2]); $("#dayHidden").val(regs[3]); $("#monthHidden").val(regs[4]); $("#weekHidden").val(regs[5]); $.fn.cronGen.tools.initObj(regs[0], "second"); $.fn.cronGen.tools.initObj(regs[1], "min"); $.fn.cronGen.tools.initObj(regs[2], "hour"); $.fn.cronGen.tools.initDay(regs[3]); $.fn.cronGen.tools.initMonth(regs[4]); $.fn.cronGen.tools.initWeek(regs[5]); if (regs.length > 6) { $("input[name=yearHidden]").val(regs[6]); $.fn.cronGen.tools.initYear(regs[6]); } } }, cronResult : function() { var result; var second = $("#secondHidden").val(); second = second== "" ? "*":second; var minute = $("#minHidden").val(); minute = minute== "" ? "*":minute; var hour = $("#hourHidden").val(); hour = hour== "" ? "*":hour; var day = $("#dayHidden").val(); day = day== "" ? "*":day; var month = $("#monthHidden").val(); month = month== "" ? "*":month; var week = $("#weekHidden").val(); week = week== "" ? "?":week; var year = $("#yearHidden").val(); if(year!="") { result = second+" "+minute+" "+hour+" "+day+" "+month+" "+week+" "+year; }else { result = second+" "+minute+" "+hour+" "+day+" "+month+" "+week; } return result; }, clearCheckbox : function(dom){ //清除选中的checkbox var list = $("."+dom+"List").children().filter(":checked"); if ($(list).length > 0) { $.each(list, function(index){ $(this).attr("checked", false); $(this).attr("disabled", "disabled"); $(this).change(); }); } }, initCheckBox : function(dom) { //移除checkbox禁用 var list = $("."+dom+"List").children(); if ($(list).length > 0) { $.each(list, function(index){ $(this).removeAttr("disabled"); }); } } }; })(jQuery); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/cronGen/cronGen_en.js ================================================ (function ($) { // var resultsName = ""; var inputElement; var displayElement; $.fn.extend({ cronGen: function (options) { if (options == null) { options = {}; } options = $.extend({}, $.fn.cronGen.defaultOptions, options); //create top menu var cronContainer = $("
      ", { id: "CronContainer", style: "display:none;width:300px;height:300px;" }); var mainDiv = $("
      ", { id: "CronGenMainDiv", style: "width:410px;height:420px;" }); var topMenu = $("
        ", { "class": "nav nav-tabs", id: "CronGenTabs" }); $('
      • ', { 'class': 'active' }).html($('')).appendTo(topMenu); $('
      • ').html($('Minute')).appendTo(topMenu); $('
      • ').html($('Hour')).appendTo(topMenu); $('
      • ').html($('Day')).appendTo(topMenu); $('
      • ').html($('Month')).appendTo(topMenu); $('
      • ').html($('Week')).appendTo(topMenu); $('
      • ').html($('Year')).appendTo(topMenu); $(topMenu).appendTo(mainDiv); //create what's inside the tabs var container = $("
        ", { "class": "container-fluid", "style": "margin-top: 30px;margin-left: -14px;" }); var row = $("
        ", { "class": "row-fluid" }); var span12 = $("
        ", { "class": "span12" }); var tabContent = $("
        ", { "class": "tab-content", "style": "border:0px; margin-top:-20px;" }); //creating the secondsTab var secondsTab = $("
        ", { "class": "tab-pane active", id: "Secondly" }); var seconds1 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "1", name : "second"}).appendTo(seconds1); $(seconds1).append("Per second, allowed wildcard[, - * /]"); $(seconds1).appendTo(secondsTab); var seconds2 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "2", name : "second"}).appendTo(seconds2); $(seconds2).append("Cycle, from"); $("",{type : "text", id : "secondStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds2); $(seconds2).append("-"); $("",{type : "text", id : "secondEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds2); $(seconds2).append("second"); $(seconds2).appendTo(secondsTab); var seconds3 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "3", name : "second"}).appendTo(seconds3); $(seconds3).append("from"); $("",{type : "text", id : "secondStart_1", value : "0", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds3); $(seconds3).append("seconds start, per"); $("",{type : "text", id : "secondEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(seconds3); $(seconds3).append("second execute once"); $(seconds3).appendTo(secondsTab); var seconds4 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "4", name : "second", id: "sencond_appoint"}).appendTo(seconds4); $(seconds4).append("specify"); $(seconds4).appendTo(secondsTab); $(secondsTab).append('
        00010203040506070809
        '); $(secondsTab).append('
        10111213141516171819
        '); $(secondsTab).append('
        20212223242526272829
        '); $(secondsTab).append('
        30313233343536373839
        '); $(secondsTab).append('
        40414243444546474849
        '); $(secondsTab).append('
        50515253545556575859
        '); $("",{type : "hidden", id : "secondHidden"}).appendTo(secondsTab); $(secondsTab).appendTo(tabContent); //creating the minutesTab var minutesTab = $("
        ", { "class": "tab-pane", id: "Minutes" }); var minutes1 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "1", name : "min"}).appendTo(minutes1); $(minutes1).append("Per minute, allowed wildcard[, - * /]"); $(minutes1).appendTo(minutesTab); var minutes2 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "2", name : "min"}).appendTo(minutes2); $(minutes2).append("Cycle, from"); $("",{type : "text", id : "minStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes2); $(minutes2).append("-"); $("",{type : "text", id : "minEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes2); $(minutes2).append("minute"); $(minutes2).appendTo(minutesTab); var minutes3 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "3", name : "min"}).appendTo(minutes3); $(minutes3).append("from"); $("",{type : "text", id : "minStart_1", value : "0", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes3); $(minutes3).append("seconds start, per"); $("",{type : "text", id : "minEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(minutes3); $(minutes3).append("second execute once"); $(minutes3).appendTo(minutesTab); var minutes4 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "4", name : "min", id: "min_appoint"}).appendTo(minutes4); $(minutes4).append("specify"); $(minutes4).appendTo(minutesTab); $(minutesTab).append('
        00010203040506070809
        '); $(minutesTab).append('
        10111213141516171819
        '); $(minutesTab).append('
        20212223242526272829
        '); $(minutesTab).append('
        30313233343536373839
        '); $(minutesTab).append('
        40414243444546474849
        '); $(minutesTab).append('
        50515253545556575859
        '); $("",{type : "hidden", id : "minHidden"}).appendTo(minutesTab); $(minutesTab).appendTo(tabContent); //creating the hourlyTab var hourlyTab = $("
        ", { "class": "tab-pane", id: "Hourly" }); var hourly1 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "1", name : "hour"}).appendTo(hourly1); $(hourly1).append("Per hour, allowed wildcard[, - * /]"); $(hourly1).appendTo(hourlyTab); var hourly2 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "2", name : "hour"}).appendTo(hourly2); $(hourly2).append("Cycle, from"); $("",{type : "text", id : "hourStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly2); $(hourly2).append("-"); $("",{type : "text", id : "hourEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly2); $(hourly2).append("hour"); $(hourly2).appendTo(hourlyTab); var hourly3 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "3", name : "hour"}).appendTo(hourly3); $(hourly3).append("from"); $("",{type : "text", id : "hourStart_1", value : "0", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly3); $(hourly3).append("hour start, per"); $("",{type : "text", id : "hourEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(hourly3); $(hourly3).append("hour execute once"); $(hourly3).appendTo(hourlyTab); var hourly4 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "4", name : "hour", id: "hour_appoint"}).appendTo(hourly4); $(hourly4).append("specify"); $(hourly4).appendTo(hourlyTab); $(hourlyTab).append('
        000102030405
        '); $(hourlyTab).append('
        060708091011
        '); $(hourlyTab).append('
        121314151617
        '); $(hourlyTab).append('
        181920212223
        '); $("",{type : "hidden", id : "hourHidden"}).appendTo(hourlyTab); $(hourlyTab).appendTo(tabContent); //creating the dailyTab var dailyTab = $("
        ", { "class": "tab-pane", id: "Daily" }); var daily1 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "1", name : "day"}).appendTo(daily1); $(daily1).append("Per day, allowed wildcard[, - * / L W]"); $(daily1).appendTo(dailyTab); var daily5 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "2", name : "day"}).appendTo(daily5); $(daily5).append("not specify"); $(daily5).appendTo(dailyTab); var daily2 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "3", name : "day"}).appendTo(daily2); $(daily2).append("Cycle, from"); $("",{type : "text", id : "dayStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily2); $(daily2).append("-"); $("",{type : "text", id : "dayEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily2); $(daily2).append("day"); $(daily2).appendTo(dailyTab); var daily3 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "4", name : "day"}).appendTo(daily3); $(daily3).append("from"); $("",{type : "text", id : "dayStart_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily3); $(daily3).append("day start, per"); $("",{type : "text", id : "dayEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily3); $(daily3).append("day execute once"); $(daily3).appendTo(dailyTab); var daily6 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "5", name : "day"}).appendTo(daily6); $(daily6).append("The most recent working day on the 1"); $("",{type : "text", id : "dayStart_2", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(daily6); $(daily6).append(" of each month"); $(daily6).appendTo(dailyTab); var daily7 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "6", name : "day"}).appendTo(daily7); $(daily7).append("The last day of the month"); $(daily7).appendTo(dailyTab); var daily4 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "7", name : "day", id: "day_appoint"}).appendTo(daily4); $(daily4).append("specify"); $(daily4).appendTo(dailyTab); $(dailyTab).append('
        01020304050607080910
        '); $(dailyTab).append('
        11121314151617181920
        '); $(dailyTab).append('
        21222324252627282930
        '); $(dailyTab).append('
        31
        '); $("",{type : "hidden", id : "dayHidden"}).appendTo(dailyTab); $(dailyTab).appendTo(tabContent); //creating the monthlyTab var monthlyTab = $("
        ", { "class": "tab-pane", id: "Monthly" }); var monthly1 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "1", name : "month"}).appendTo(monthly1); $(monthly1).append("Per month, allowed wildcard[, - * /]"); $(monthly1).appendTo(monthlyTab); var monthly2 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "2", name : "month"}).appendTo(monthly2); $(monthly2).append("not specify"); $(monthly2).appendTo(monthlyTab); var monthly3 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "3", name : "month"}).appendTo(monthly3); $(monthly3).append("Cycle, from"); $("",{type : "text", id : "monthStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly3); $(monthly3).append("-"); $("",{type : "text", id : "monthEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly3); $(monthly3).append("month"); $(monthly3).appendTo(monthlyTab); var monthly4 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "4", name : "month"}).appendTo(monthly4); $(monthly4).append("Starting from "); $("",{type : "text", id : "monthStart_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly4); $(monthly4).append("day, once every"); $("",{type : "text", id : "monthEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(monthly4); $(monthly4).append("month"); $(monthly4).appendTo(monthlyTab); var monthly5 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "5", name : "month", id: "month_appoint"}).appendTo(monthly5); $(monthly5).append("specify"); $(monthly5).appendTo(monthlyTab); $(monthlyTab).append('
        010203040506
        '); $(monthlyTab).append('
        070809101112
        '); $("",{type : "hidden", id : "monthHidden"}).appendTo(monthlyTab); $(monthlyTab).appendTo(tabContent); //creating the weeklyTab var weeklyTab = $("
        ", { "class": "tab-pane", id: "Weekly" }); var weekly1 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "1", name : "week"}).appendTo(weekly1); $(weekly1).append("Per week, allowed wildcard[, - * / L #]"); $(weekly1).appendTo(weeklyTab); var weekly2 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "2", name : "week"}).appendTo(weekly2); $(weekly2).append("not specify"); $(weekly2).appendTo(weeklyTab); var weekly3 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "3", name : "week"}).appendTo(weekly3); $(weekly3).append("Cycle, from week"); $("",{type : "text", id : "weekStart_0", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly3); $(weekly3).append("-"); $("",{type : "text", id : "weekEnd_0", value : "2", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly3); $(weekly3).appendTo(weeklyTab); var weekly4 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "4", name : "week"}).appendTo(weekly4); $(weekly4).append("The"); $("",{type : "text", id : "weekStart_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly4); $(weekly4).append("th week, and day "); $("",{type : "text", id : "weekEnd_1", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly4); $(weekly4).appendTo(weeklyTab); var weekly5 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "5", name : "week"}).appendTo(weekly5); $(weekly5).append("Last week of the month"); $("",{type : "text", id : "weekStart_2", value : "1", style:"width:35px; height:20px; text-align: center; margin: 0 3px;"}).appendTo(weekly5); $(weekly5).appendTo(weeklyTab); var weekly6 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "6", name : "week", id: "week_appoint"}).appendTo(weekly6); $(weekly6).append("specify"); $(weekly6).appendTo(weeklyTab); $(weeklyTab).append('
        1234567
        '); $("",{type : "hidden", id : "weekHidden"}).appendTo(weeklyTab); $(weeklyTab).appendTo(tabContent); //creating the yearlyTab var yearlyTab = $("
        ", { "class": "tab-pane", id: "Yearly" }); var yearly1 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "1", name : "year"}).appendTo(yearly1); $(yearly1).append("not specify allowed wildcard[, - * /] not required"); $(yearly1).appendTo(yearlyTab); var yearly3 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "2", name : "year"}).appendTo(yearly3); $(yearly3).append("Per year"); $(yearly3).appendTo(yearlyTab); var yearly2 = $("
        ",{"class":"line"}); $("",{type : "radio", value : "3", name : "year"}).appendTo(yearly2); $(yearly2).append("Cycle, from "); $("",{type : "text", id : "yearStart_0", value : "2016", style:"width:45px; height:20px;"}).appendTo(yearly2); $(yearly2).append("-"); $("",{type : "text", id : "yearEnd_0", value : "2017", style:"width:45px; height:20px;"}).appendTo(yearly2); $(yearly2).append("year"); $(yearly2).appendTo(yearlyTab); $("",{type : "hidden", id : "yearHidden"}).appendTo(yearlyTab); $(yearlyTab).appendTo(tabContent); $(tabContent).appendTo(span12); //creating the button and results input // resultsName = $(this).prop("id"); // $(this).prop("name", resultsName); var runTime = '

        '; $(span12).appendTo(row); $(row).appendTo(container); $(container).appendTo(mainDiv); $(runTime).appendTo(mainDiv); $(cronContainer).append(mainDiv); var that = $(this); // Hide the original input that.hide(); // Replace the input with an input group var $g = $("
        ").addClass("input-group"); // Add an input var $i = $("", { type: 'text', placeholder: 'cron expression...', name: 'cronGen_display' }).addClass("form-control").val($(that).val()); $i.appendTo($g); // Add the button var $b = $(""); // Put button inside span var $s = $("").addClass("input-group-btn"); $b.appendTo($s); $s.appendTo($g); $(this).before($g); inputElement = that; displayElement = $i; $b.popover({ html: true, content: function () { return $(cronContainer).html(); }, template: '

        ', sanitize:false, placement: options.direction }).on('click', function (e) { if (inputElement.val().trim() !== '') { refreshRunTime(); } e.preventDefault(); //fillDataOfMinutesAndHoursSelectOptions(); //fillDayWeekInMonth(); //fillInWeekDays(); //fillInMonths(); $.fn.cronGen.tools.cronParse(inputElement.val()); //绑定指定事件 $.fn.cronGen.tools.initChangeEvent(); $('#CronGenTabs a').click(function (e) { e.preventDefault(); $(this).tab('show'); //generate(); }); $("#CronGenMainDiv select,input").change(function (e) { generate(); refreshRunTime(); }); $("#CronGenMainDiv input").focus(function (e) { generate(); }); //generate(); }); return; } }); var fillInMonths = function () { var days = [ { text: "January", val: "1" }, { text: "February", val: "2" }, { text: "March", val: "3" }, { text: "April", val: "4" }, { text: "May", val: "5" }, { text: "June", val: "6" }, { text: "July", val: "7" }, { text: "August", val: "8" }, { text: "September", val: "9" }, { text: "October", val: "10" }, { text: "November", val: "11" }, { text: "December", val: "12" } ]; $(".months").each(function () { fillOptions(this, days); }); }; var fillOptions = function (elements, options) { for (var i = 0; i < options.length; i++) $(elements).append(""); }; var fillDataOfMinutesAndHoursSelectOptions = function () { for (var i = 0; i < 60; i++) { if (i < 24) { $(".hours").each(function () { $(this).append(timeSelectOption(i)); }); } $(".minutes").each(function () { $(this).append(timeSelectOption(i)); }); } }; var fillInWeekDays = function () { var days = [ { text: "Tuesday", val: "2" }, { text: "Wednesday", val: "3" }, { text: "Thursday", val: "4" }, { text: "Friday", val: "5" }, { text: "Saturday", val: "6" }, { text: "Sunday", val: "7" }, { text: "Monday", val: "1" } ]; $(".week-days").each(function () { fillOptions(this, days); }); }; var fillDayWeekInMonth = function () { var days = [ { text: "First", val: "1" }, { text: "Second", val: "2" }, { text: "Third", val: "3" }, { text: "Fourth", val: "4" } ]; $(".day-order-in-month").each(function () { fillOptions(this, days); }); }; var displayTimeUnit = function (unit) { if (unit.toString().length == 1) return "0" + unit; return unit; }; var timeSelectOption = function (i) { return ""; }; var generate = function () { var activeTab = $("ul#CronGenTabs li.active a").prop("id"); if (activeTab == undefined) { return; } var results = ""; switch (activeTab) { case "SecondlyTab": switch ($("input:radio[name=second]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("second"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.cycle("second"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.startOn("second"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.initCheckBox("second"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "MinutesTab": switch ($("input:radio[name=min]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("min"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.cycle("min"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.startOn("min"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.initCheckBox("min"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "HourlyTab": switch ($("input:radio[name=hour]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("hour"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.cycle("hour"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.startOn("hour"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.initCheckBox("hour"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "DailyTab": switch ($("input:radio[name=day]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("day"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.unAppoint("day"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("day"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.startOn("day"); results = $.fn.cronGen.tools.cronResult(); break; case "5": $.fn.cronGen.tools.workDay("day"); results = $.fn.cronGen.tools.cronResult(); break; case "6": $.fn.cronGen.tools.lastDay("day"); results = $.fn.cronGen.tools.cronResult(); break; case "7": $.fn.cronGen.tools.initCheckBox("day"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "WeeklyTab": switch ($("input:radio[name=week]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("week"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.unAppoint("week"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("week"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.startOn("week"); results = $.fn.cronGen.tools.cronResult(); break; case "5": $.fn.cronGen.tools.lastWeek("week"); results = $.fn.cronGen.tools.cronResult(); break; case "6": $.fn.cronGen.tools.initCheckBox("week"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "MonthlyTab": switch ($("input:radio[name=month]:checked").val()) { case "1": $.fn.cronGen.tools.everyTime("month"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.unAppoint("month"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("month"); results = $.fn.cronGen.tools.cronResult(); break; case "4": $.fn.cronGen.tools.startOn("month"); results = $.fn.cronGen.tools.cronResult(); break; case "5": $.fn.cronGen.tools.initCheckBox("month"); results = $.fn.cronGen.tools.cronResult(); break; } break; case "YearlyTab": switch ($("input:radio[name=year]:checked").val()) { case "1": $.fn.cronGen.tools.unAppoint("year"); results = $.fn.cronGen.tools.cronResult(); break; case "2": $.fn.cronGen.tools.everyTime("year"); results = $.fn.cronGen.tools.cronResult(); break; case "3": $.fn.cronGen.tools.cycle("year"); results = $.fn.cronGen.tools.cronResult(); break; } break; } // Update original control inputElement.val(results); // Update display displayElement.val(results); }; var refreshRunTime = function () { $.ajax({ type : 'GET', url : base_url + "/jobinfo/nextTriggerTime", data : { "scheduleType" : 'CRON', "scheduleConf" : inputElement.val() }, dataType : "json", success : function(data){ if (data.code === 200) { $('#runTime').val(data.content.join("\n")); } else { $('#runTime').val(data.msg); } } }); }; })(jQuery); (function($) { $.fn.cronGen.defaultOptions = { direction : 'bottom' }; $.fn.cronGen.tools = { /** * 每周期 */ everyTime : function(dom){ $("#"+dom+"Hidden").val("*"); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 不指定 */ unAppoint : function(dom){ var val = "?"; if (dom == "year") { val = ""; } $("#"+dom+"Hidden").val(val); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 周期 */ cycle : function(dom){ var start = $("#"+dom+"Start_0").val(); var end = $("#"+dom+"End_0").val(); $("#"+dom+"Hidden").val(start + "-" + end); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 从开始 */ startOn : function(dom) { var start = $("#"+dom+"Start_1").val(); var end = $("#"+dom+"End_1").val(); $("#"+dom+"Hidden").val(start + "/" + end); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 最后一天 */ lastDay : function(dom){ $("#"+dom+"Hidden").val("L"); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 每周的某一天 */ weekOfDay : function(dom){ var start = $("#"+dom+"Start_0").val(); var end = $("#"+dom+"End_0").val(); $("#"+dom+"Hidden").val(start + "#" + end); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 最后一周 */ lastWeek : function(dom){ var start = $("#"+dom+"Start_2").val(); $("#"+dom+"Hidden").val(start+"L"); $.fn.cronGen.tools.clearCheckbox(dom); }, /** * 工作日 */ workDay : function(dom) { var start = $("#"+dom+"Start_2").val(); $("#"+dom+"Hidden").val(start + "W"); $.fn.cronGen.tools.clearCheckbox(dom); }, initChangeEvent : function(){ var secondList = $(".secondList").children(); $("#sencond_appoint").click(function(){ if (this.checked) { if ($(secondList).filter(":checked").length == 0) { $(secondList.eq(0)).attr("checked", true); } secondList.eq(0).change(); } }); secondList.change(function() { var sencond_appoint = $("#sencond_appoint").prop("checked"); if (sencond_appoint) { var vals = []; secondList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 59) { val = vals.join(","); }else if(vals.length == 59){ val = "*"; } $("#secondHidden").val(val); } }); var minList = $(".minList").children(); $("#min_appoint").click(function(){ if (this.checked) { if ($(minList).filter(":checked").length == 0) { $(minList.eq(0)).attr("checked", true); } minList.eq(0).change(); } }); minList.change(function() { var min_appoint = $("#min_appoint").prop("checked"); if (min_appoint) { var vals = []; minList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 59) { val = vals.join(","); }else if(vals.length == 59){ val = "*"; } $("#minHidden").val(val); } }); var hourList = $(".hourList").children(); $("#hour_appoint").click(function(){ if (this.checked) { if ($(hourList).filter(":checked").length == 0) { $(hourList.eq(0)).attr("checked", true); } hourList.eq(0).change(); } }); hourList.change(function() { var hour_appoint = $("#hour_appoint").prop("checked"); if (hour_appoint) { var vals = []; hourList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 24) { val = vals.join(","); }else if(vals.length == 24){ val = "*"; } $("#hourHidden").val(val); } }); var dayList = $(".dayList").children(); $("#day_appoint").click(function(){ if (this.checked) { if ($(dayList).filter(":checked").length == 0) { $(dayList.eq(0)).attr("checked", true); } dayList.eq(0).change(); } }); dayList.change(function() { var day_appoint = $("#day_appoint").prop("checked"); if (day_appoint) { var vals = []; dayList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 31) { val = vals.join(","); }else if(vals.length == 31){ val = "*"; } $("#dayHidden").val(val); } }); var monthList = $(".monthList").children(); $("#month_appoint").click(function(){ if (this.checked) { if ($(monthList).filter(":checked").length == 0) { $(monthList.eq(0)).attr("checked", true); } monthList.eq(0).change(); } }); monthList.change(function() { var month_appoint = $("#month_appoint").prop("checked"); if (month_appoint) { var vals = []; monthList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 12) { val = vals.join(","); }else if(vals.length == 12){ val = "*"; } $("#monthHidden").val(val); } }); var weekList = $(".weekList").children(); $("#week_appoint").click(function(){ if (this.checked) { if ($(weekList).filter(":checked").length == 0) { $(weekList.eq(0)).attr("checked", true); } weekList.eq(0).change(); } }); weekList.change(function() { var week_appoint = $("#week_appoint").prop("checked"); if (week_appoint) { var vals = []; weekList.each(function() { if (this.checked) { vals.push(this.value); } }); var val = "?"; if (vals.length > 0 && vals.length < 7) { val = vals.join(","); }else if(vals.length == 7){ val = "*"; } $("#weekHidden").val(val); } }); }, initObj : function(strVal, strid){ var ary = null; var objRadio = $("input[name='" + strid + "'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(1).attr("checked", "checked"); $("#" + strid + "Start_0").val(ary[0]); $("#" + strid + "End_0").val(ary[1]); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(2).attr("checked", "checked"); $("#" + strid + "Start_1").val(ary[0]); $("#" + strid + "End_1").val(ary[1]); } else { objRadio.eq(3).attr("checked", "checked"); if (strVal != "?") { ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $("." + strid + "List input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox(strid); } } }, initDay : function(strVal) { var ary = null; var objRadio = $("input[name='day'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal == "?") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(2).attr("checked", "checked"); $("#dayStart_0").val(ary[0]); $("#dayEnd_0").val(ary[1]); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(3).attr("checked", "checked"); $("#dayStart_1").val(ary[0]); $("#dayEnd_1").val(ary[1]); } else if (strVal.split('W').length > 1) { ary = strVal.split('W'); objRadio.eq(4).attr("checked", "checked"); $("#dayStart_2").val(ary[0]); } else if (strVal == "L") { objRadio.eq(5).attr("checked", "checked"); } else { objRadio.eq(6).attr("checked", "checked"); ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $(".dayList input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox("day"); } }, initMonth : function(strVal) { var ary = null; var objRadio = $("input[name='month'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal == "?") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(2).attr("checked", "checked"); $("#monthStart_0").val(ary[0]); $("#monthEnd_0").val(ary[1]); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(3).attr("checked", "checked"); $("#monthStart_1").val(ary[0]); $("#monthEnd_1").val(ary[1]); } else { objRadio.eq(4).attr("checked", "checked"); ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $(".monthList input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox("month"); } }, initWeek : function(strVal) { var ary = null; var objRadio = $("input[name='week'"); if (strVal == "*") { objRadio.eq(0).attr("checked", "checked"); } else if (strVal == "?") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('/').length > 1) { ary = strVal.split('/'); objRadio.eq(2).attr("checked", "checked"); $("#weekStart_0").val(ary[0]); $("#weekEnd_0").val(ary[1]); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(3).attr("checked", "checked"); $("#weekStart_1").val(ary[0]); $("#weekEnd_1").val(ary[1]); } else if (strVal.split('L').length > 1) { ary = strVal.split('L'); objRadio.eq(4).attr("checked", "checked"); $("#weekStart_2").val(ary[0]); } else { objRadio.eq(5).attr("checked", "checked"); ary = strVal.split(","); for (var i = 0; i < ary.length; i++) { $(".weekList input[value='" + ary[i] + "']").attr("checked", "checked"); } $.fn.cronGen.tools.initCheckBox("week"); } }, initYear : function(strVal) { var ary = null; var objRadio = $("input[name='year'"); if (strVal == "*") { objRadio.eq(1).attr("checked", "checked"); } else if (strVal.split('-').length > 1) { ary = strVal.split('-'); objRadio.eq(2).attr("checked", "checked"); $("#yearStart_0").val(ary[0]); $("#yearEnd_0").val(ary[1]); } }, cronParse : function(cronExpress) { //获取参数中表达式的值 if (cronExpress) { var regs = cronExpress.split(' '); $("#secondHidden").val(regs[0]); $("#minHidden").val(regs[1]); $("#hourHidden").val(regs[2]); $("#dayHidden").val(regs[3]); $("#monthHidden").val(regs[4]); $("#weekHidden").val(regs[5]); $.fn.cronGen.tools.initObj(regs[0], "second"); $.fn.cronGen.tools.initObj(regs[1], "min"); $.fn.cronGen.tools.initObj(regs[2], "hour"); $.fn.cronGen.tools.initDay(regs[3]); $.fn.cronGen.tools.initMonth(regs[4]); $.fn.cronGen.tools.initWeek(regs[5]); if (regs.length > 6) { $("input[name=yearHidden]").val(regs[6]); $.fn.cronGen.tools.initYear(regs[6]); } } }, cronResult : function() { var result; var second = $("#secondHidden").val(); second = second== "" ? "*":second; var minute = $("#minHidden").val(); minute = minute== "" ? "*":minute; var hour = $("#hourHidden").val(); hour = hour== "" ? "*":hour; var day = $("#dayHidden").val(); day = day== "" ? "*":day; var month = $("#monthHidden").val(); month = month== "" ? "*":month; var week = $("#weekHidden").val(); week = week== "" ? "?":week; var year = $("#yearHidden").val(); if(year!="") { result = second+" "+minute+" "+hour+" "+day+" "+month+" "+week+" "+year; }else { result = second+" "+minute+" "+hour+" "+day+" "+month+" "+week; } return result; }, clearCheckbox : function(dom){ //清除选中的checkbox var list = $("."+dom+"List").children().filter(":checked"); if ($(list).length > 0) { $.each(list, function(index){ $(this).attr("checked", false); $(this).attr("disabled", "disabled"); $(this).change(); }); } }, initCheckBox : function(dom) { //移除checkbox禁用 var list = $("."+dom+"List").children(); if ($(list).length > 0) { $.each(list, function(index){ $(this).removeAttr("disabled"); }); } } }; })(jQuery); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/jquery/jquery.cookie.js ================================================ /*! * jQuery Cookie Plugin v1.4.1 * https://github.com/carhartl/jquery-cookie * * Copyright 2013 Klaus Hartl * Released under the MIT license */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery'], factory); } else if (typeof exports === 'object') { // CommonJS factory(require('jquery')); } else { // Browser globals factory(jQuery); } }(function ($) { var pluses = /\+/g; function encode(s) { return config.raw ? s : encodeURIComponent(s); } function decode(s) { return config.raw ? s : decodeURIComponent(s); } function stringifyCookieValue(value) { return encode(config.json ? JSON.stringify(value) : String(value)); } function parseCookieValue(s) { if (s.indexOf('"') === 0) { // This is a quoted cookie as according to RFC2068, unescape... s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); } try { // Replace server-side written pluses with spaces. // If we can't decode the cookie, ignore it, it's unusable. // If we can't parse the cookie, ignore it, it's unusable. s = decodeURIComponent(s.replace(pluses, ' ')); return config.json ? JSON.parse(s) : s; } catch(e) {} } function read(s, converter) { var value = config.raw ? s : parseCookieValue(s); return $.isFunction(converter) ? converter(value) : value; } var config = $.cookie = function (key, value, options) { // Write if (value !== undefined && !$.isFunction(value)) { options = $.extend({}, config.defaults, options); if (typeof options.expires === 'number') { var days = options.expires, t = options.expires = new Date(); t.setTime(+t + days * 864e+5); } return (document.cookie = [ encode(key), '=', stringifyCookieValue(value), options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE options.path ? '; path=' + options.path : '', options.domain ? '; domain=' + options.domain : '', options.secure ? '; secure' : '' ].join('')); } // Read var result = key ? undefined : {}; // To prevent the for loop in the first place assign an empty array // in case there are no cookies at all. Also prevents odd result when // calling $.cookie(). var cookies = document.cookie ? document.cookie.split('; ') : []; for (var i = 0, l = cookies.length; i < l; i++) { var parts = cookies[i].split('='); var name = decode(parts.shift()); var cookie = parts.join('='); if (key && key === name) { // If second argument (value) is a function it's a converter... result = read(cookie, value); break; } // Prevent storing a cookie that we couldn't decode. if (!key && (cookie = read(cookie)) !== undefined) { result[name] = cookie; } } return result; }; config.defaults = {}; $.removeCookie = function (key, options) { if ($.cookie(key) === undefined) { return false; } // Must not alter options, thus extending a fresh object... $.cookie(key, '', $.extend({}, options, { expires: -1 })); return !$.cookie(key); }; })); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/layer/layer.js ================================================ /*! layer-v3.1.1 Web弹层组件 MIT License http://layer.layui.com/ By 贤心 */ ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
        '+(f?r.title[0]:r.title)+"
        ":"";return r.zIndex=s,t([r.shade?'
        ':"",'
        '+(e&&2!=r.type?"":u)+'
        '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
        '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
        '+e+"
        "}():"")+(r.resize?'':"")+"
        "],u,i('
        ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){s=e.find(".layui-layer-input"),s.focus(),"function"==typeof f&&f(e)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"
        ";return a}(),content:'
          '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
        • '+(t[0].content||"no content")+"
        • ";i'+(t[i].content||"no content")+"";return a}()+"
        ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
        '+(u.length>1?'':"")+'
        '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
        ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
        是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/plugins/layer/theme/default/layer.css ================================================ .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}html #layuicss-layer{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;border-radius:2px;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layer-anim{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-00{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 15px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:5px 5px 0;padding:0 15px;border:1px solid #dedede;background-color:#fff;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#1E9FFF;background-color:#1E9FFF;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:8px 15px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:5px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#fff;border-color:#E9E7E7;color:#333}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95;border-color:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:230px;height:36px;margin:0 auto;line-height:30px;padding-left:10px;border:1px solid #e6e6e6;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px;padding:6px 10px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;overflow:hidden;cursor:pointer}.layui-layer-tab .layui-layer-title span.layui-this{height:43px;border-left:1px solid #eee;border-right:1px solid #eee;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.layui-this{display:block}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/common/common.exception.ftl ================================================ Error

        System Error

        ${exceptionMsg}

        Back

        ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/common/common.macro.ftl ================================================ <#macro commonStyle> <#-- favicon --> <#-- i18n --> <#global I18n = I18nUtil.getMultString()?eval /> <#macro commonScript> <#-- jquery cookie --> <#-- jquery.validate --> <#-- layer --> <#-- common --> <#macro commonHeader>
        <#macro commonLeft pageName > <#macro commonControl >
        <#macro commonFooter >
        ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/help.ftl ================================================ <#import "./common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} sidebar-collapse ">
        <@netCommon.commonHeader /> <@netCommon.commonLeft "help" />

        ${I18n.job_help}

        ${I18n.admin_name_full}


        Github    

        ${I18n.job_help_document}

        <@netCommon.commonFooter />
        <@netCommon.commonScript /> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/index.ftl ================================================ <#import "./common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} sidebar-collapse ">
        <@netCommon.commonHeader /> <@netCommon.commonLeft "index" />

        ${I18n.job_dashboard_name}

        <#-- 任务信息 -->
        ${I18n.job_dashboard_job_num} ${jobInfoCount}
        ${I18n.job_dashboard_job_num_tip}
        <#-- 调度信息 -->
        ${I18n.job_dashboard_trigger_num} ${jobLogCount}
        ${I18n.job_dashboard_trigger_num_tip} <#--<#if jobLogCount gt 0> 调度成功率:${(jobLogSuccessCount*100/jobLogCount)?string("0.00")}% -->
        <#-- 执行器 -->
        ${I18n.job_dashboard_jobgroup_num} ${executorCount}
        ${I18n.job_dashboard_jobgroup_num_tip}
        <#-- 调度报表:时间区间筛选,左侧折线图 + 右侧饼图 -->

        ${I18n.job_dashboard_report}

        <#---->
        <#---->
        <#-- 左侧折线图 -->
        <#-- 右侧饼图 -->
        <@netCommon.commonFooter />
        <@netCommon.commonScript /> <#-- echarts --> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/jobcode/jobcode.index.ftl ================================================ <#import "../common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} <@netCommon.commonScript /> <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" /> <#assign glueTypeIdeMode = "text/x-java" /> <#if jobInfo.glueType == "GLUE_GROOVY" > <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" /> <#assign glueTypeIdeMode = "text/x-java" /> <#elseif jobInfo.glueType == "GLUE_SHELL" > <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/shell/shell.js" /> <#assign glueTypeIdeMode = "text/x-sh" /> <#elseif jobInfo.glueType == "GLUE_PYTHON" > <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/python/python.js" /> <#assign glueTypeIdeMode = "text/x-python" /> <#elseif jobInfo.glueType == "GLUE_PHP" > <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/php/php.js" /> <#assign glueTypeIdeMode = "text/x-php" /> <#assign glueTypeModeSrc02 = "${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js" /> <#elseif jobInfo.glueType == "GLUE_NODEJS" > <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/javascript/javascript.js" /> <#assign glueTypeIdeMode = "text/javascript" /> <#elseif jobInfo.glueType == "GLUE_POWERSHELL" > <#assign glueTypeModeSrc = "${request.contextPath}/static/plugins/codemirror/mode/powershell/powershell.js" /> <#assign glueTypeIdeMode = "powershell" /> <#if glueTypeModeSrc02?exists> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/jobgroup/jobgroup.index.ftl ================================================ <#import "../common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} sidebar-collapse ">
        <@netCommon.commonHeader /> <@netCommon.commonLeft "jobgroup" />

        ${I18n.jobgroup_name}

        AppName
        ${I18n.jobgroup_field_title}
        ID AppName ${I18n.jobgroup_field_title} ${I18n.jobgroup_field_addressType} OnLine ${I18n.jobgroup_field_registryList} ${I18n.system_opt}
        <@netCommon.commonFooter />
        <@netCommon.commonScript /> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/jobinfo/jobinfo.index.ftl ================================================ <#import "../common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} sidebar-collapse">
        <@netCommon.commonHeader /> <@netCommon.commonLeft "jobinfo" />

        ${I18n.jobinfo_name}

        ${I18n.jobinfo_field_jobgroup}
        <#--

        调度列表

        -->
        ${I18n.jobinfo_field_id} ${I18n.jobinfo_field_jobgroup} ${I18n.jobinfo_field_jobdesc} ${I18n.schedule_type} ${I18n.jobinfo_field_gluetype} ${I18n.jobinfo_field_executorparam} addTime updateTime ${I18n.jobinfo_field_author} ${I18n.jobinfo_field_alarmemail} ${I18n.system_status} ${I18n.system_opt}
        <@netCommon.commonFooter />
        <#-- trigger --> <@netCommon.commonScript /> <#-- cronGen --> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl ================================================ <#import "../common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name}
                        
      • <@netCommon.commonFooter />
        <@netCommon.commonScript /> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.index.ftl ================================================ <#import "../common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} sidebar-collapse ">
        <@netCommon.commonHeader /> <@netCommon.commonLeft "joblog" />

        ${I18n.joblog_name}

        ${I18n.jobinfo_field_jobgroup}
        ${I18n.jobinfo_job}
        ${I18n.joblog_status}
        ${I18n.joblog_field_triggerTime}
        <#--

        调度日志

        -->
        <#---->
        ${I18n.jobinfo_field_id} jobGroup执行器地址 运行模式 任务参数${I18n.joblog_field_triggerTime} ${I18n.joblog_field_triggerCode} ${I18n.joblog_field_triggerMsg} ${I18n.joblog_field_handleTime} ${I18n.joblog_field_handleCode} ${I18n.joblog_field_handleMsg} ${I18n.system_opt}
        <@netCommon.commonFooter />
        <@netCommon.commonScript /> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/login.ftl ================================================ <#import "./common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} <@netCommon.commonScript /> ================================================ FILE: ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/user/user.index.ftl ================================================ <#import "../common/common.macro.ftl" as netCommon> <@netCommon.commonStyle /> ${I18n.admin_name} sidebar-collapse">
        <@netCommon.commonHeader /> <@netCommon.commonLeft "user" />

        ${I18n.user_manage}

        ${I18n.user_role}
        ${I18n.user_username}
        ID ${I18n.user_username} ${I18n.user_password} ${I18n.user_role} ${I18n.user_permission} ${I18n.system_opt}
        <@netCommon.commonFooter />
        <@netCommon.commonScript /> ================================================ FILE: ruoyi-framework/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 ruoyi-framework framework框架核心 org.springframework.boot spring-boot-starter-web spring-boot-starter-tomcat org.springframework.boot org.springframework.boot spring-boot-starter-undertow org.springframework.boot spring-boot-starter-aop p6spy p6spy org.springframework.boot spring-boot-starter-actuator de.codecentric spring-boot-admin-starter-client com.alibaba transmittable-thread-local com.ruoyi ruoyi-common ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/aspectj/LogAspect.java ================================================ package top.flya.framework.aspectj; import cn.hutool.core.lang.Dict; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import top.flya.common.annotation.Log; import top.flya.common.core.domain.event.OperLogEvent; import top.flya.common.enums.BusinessStatus; import top.flya.common.enums.HttpMethod; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; 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.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collection; import java.util.Map; /** * 操作日志记录处理 * * @author Lion Li */ @Slf4j @Aspect @Component public class LogAspect { /** * 排除敏感属性字段 */ public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; /** * 处理完请求后执行 * * @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 { // *========数据库日志=========*// OperLogEvent operLog = new OperLogEvent(); operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 请求的地址 String ip = ServletUtils.getClientIP(); operLog.setOperIp(ip); operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); operLog.setOperName(LoginHelper.getUsername()); if (e != null) { operLog.setStatus(BusinessStatus.FAIL.ordinal()); operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 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); // 发布事件保存数据库 SpringUtils.context().publishEvent(operLog); } catch (Exception exp) { // 记录本地异常日志 log.error("异常信息:{}", exp.getMessage()); exp.printStackTrace(); } } /** * 获取注解中对方法的描述信息 用于Controller层注解 * * @param log 日志 * @param operLog 操作日志 * @throws Exception */ public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent 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() && ObjectUtil.isNotNull(jsonResult)) { operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000)); } } /** * 获取请求的参数,放到log中 * * @param operLog 操作日志 * @throws Exception 异常 */ private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); String requestMethod = operLog.getRequestMethod(); if (MapUtil.isEmpty(paramsMap) && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); operLog.setOperParam(StringUtils.substring(params, 0, 2000)); } else { MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); MapUtil.removeAny(paramsMap, excludeParamNames); operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000)); } } /** * 参数拼装 */ private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) { StringBuilder params = new StringBuilder(); if (paramsArray != null && paramsArray.length > 0) { for (Object o : paramsArray) { if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { try { String str = JsonUtils.toJsonString(o); Dict dict = JsonUtils.parseMap(str); if (MapUtil.isNotEmpty(dict)) { MapUtil.removeAny(dict, EXCLUDE_PROPERTIES); MapUtil.removeAny(dict, excludeParamNames); str = JsonUtils.toJsonString(dict); } params.append(str).append(" "); } catch (Exception e) { e.printStackTrace(); } } } } return params.toString().trim(); } /** * 判断是否需要过滤的对象。 * * @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; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/aspectj/RateLimiterAspect.java ================================================ package top.flya.framework.aspectj; import cn.hutool.core.util.ArrayUtil; import top.flya.common.annotation.RateLimiter; import top.flya.common.constant.CacheConstants; import top.flya.common.enums.LimitType; import top.flya.common.exception.ServiceException; import top.flya.common.utils.MessageUtils; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.RedisUtils; import lombok.extern.slf4j.Slf4j; 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.redisson.api.RateType; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 限流处理 * * @author Lion Li */ @Slf4j @Aspect @Component public class RateLimiterAspect { /** * 定义spel表达式解析器 */ private final ExpressionParser parser = new SpelExpressionParser(); /** * 定义spel解析模版 */ private final ParserContext parserContext = new TemplateParserContext(); /** * 定义spel上下文对象进行解析 */ private final EvaluationContext context = new StandardEvaluationContext(); /** * 方法参数解析器 */ private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer(); @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); try { RateType rateType = RateType.OVERALL; if (rateLimiter.limitType() == LimitType.CLUSTER) { rateType = RateType.PER_CLIENT; } long number = RedisUtils.rateLimiter(combineKey, rateType, count, time); if (number == -1) { String message = rateLimiter.message(); if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) { message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1)); } throw new ServiceException(message); } log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey); } catch (Exception e) { if (e instanceof ServiceException) { throw e; } else { throw new RuntimeException("服务器限流异常,请稍候再试"); } } } public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { String key = rateLimiter.key(); // 获取方法(通过方法签名来获取) MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); Class targetClass = method.getDeclaringClass(); // 判断是否是spel格式 if (StringUtils.containsAny(key, "#")) { // 获取参数值 Object[] args = point.getArgs(); // 获取方法上参数的名称 String[] parameterNames = pnd.getParameterNames(method); if (ArrayUtil.isEmpty(parameterNames)) { throw new ServiceException("限流key解析异常!请联系管理员!"); } for (int i = 0; i < parameterNames.length; i++) { context.setVariable(parameterNames[i], args[i]); } // 解析返回给key try { Expression expression; if (StringUtils.startsWith(key, parserContext.getExpressionPrefix()) && StringUtils.endsWith(key, parserContext.getExpressionSuffix())) { expression = parser.parseExpression(key, parserContext); } else { expression = parser.parseExpression(key); } key = expression.getValue(context, String.class) + ":"; } catch (Exception e) { throw new ServiceException("限流key解析异常!请联系管理员!"); } } StringBuilder stringBuffer = new StringBuilder(CacheConstants.RATE_LIMIT_KEY); stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":"); if (rateLimiter.limitType() == LimitType.IP) { // 获取请求ip stringBuffer.append(ServletUtils.getClientIP()).append(":"); } else if (rateLimiter.limitType() == LimitType.CLUSTER) { // 获取客户端实例id stringBuffer.append(RedisUtils.getClient().getId()).append(":"); } return stringBuffer.append(key).toString(); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/aspectj/RepeatSubmitAspect.java ================================================ package top.flya.framework.aspectj; import cn.dev33.satoken.SaManager; import cn.hutool.core.util.ObjectUtil; import cn.hutool.crypto.SecureUtil; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.constant.CacheConstants; import top.flya.common.core.domain.R; import top.flya.common.exception.ServiceException; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.MessageUtils; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.RedisUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; 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.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.time.Duration; import java.util.Collection; import java.util.Map; /** * 防止重复提交(参考美团GTIS防重系统) * * @author Lion Li */ @Slf4j @RequiredArgsConstructor @Aspect @Component public class RepeatSubmitAspect { private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); @Before("@annotation(repeatSubmit)") public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { // 如果注解不为0 则使用注解数值 long interval = 0; if (repeatSubmit.interval() > 0) { interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval()); } if (interval < 1000) { throw new ServiceException("重复提交间隔时间不能小于'1'秒"); } HttpServletRequest request = ServletUtils.getRequest(); String nowParams = argsArrayToString(point.getArgs()); // 请求地址(作为存放cache的key值) String url = request.getRequestURI(); // 唯一值(没有消息头则使用请求地址) String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName())); submitKey = SecureUtil.md5(submitKey + ":" + nowParams); // 唯一标识(指定key + url + 消息头) String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; String key = RedisUtils.getCacheObject(cacheRepeatKey); if (key == null) { RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval)); KEY_CACHE.set(cacheRepeatKey); } else { String message = repeatSubmit.message(); if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) { message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1)); } throw new ServiceException(message); } } /** * 处理完请求后执行 * * @param joinPoint 切点 */ @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult") public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) { if (jsonResult instanceof R) { try { R r = (R) jsonResult; // 成功则不删除redis数据 保证在有效时间内无法重复提交 if (r.getCode() == R.SUCCESS) { return; } RedisUtils.deleteObject(KEY_CACHE.get()); } finally { KEY_CACHE.remove(); } } } /** * 拦截异常操作 * * @param joinPoint 切点 * @param e 异常 */ @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) { RedisUtils.deleteObject(KEY_CACHE.get()); KEY_CACHE.remove(); } /** * 参数拼装 */ private String argsArrayToString(Object[] paramsArray) { StringBuilder params = new StringBuilder(); if (paramsArray != null && paramsArray.length > 0) { for (Object o : paramsArray) { if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { try { params.append(JsonUtils.toJsonString(o)).append(" "); } catch (Exception e) { e.printStackTrace(); } } } } return params.toString().trim(); } /** * 判断是否需要过滤的对象。 * * @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; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/ApplicationConfig.java ================================================ package top.flya.framework.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * 程序注解配置 * * @author Lion Li */ @Configuration // 表示通过aop框架暴露该代理对象,AopContext能够访问 @EnableAspectJAutoProxy(exposeProxy = true) public class ApplicationConfig { } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/AsyncConfig.java ================================================ package top.flya.framework.config; import cn.hutool.core.util.ArrayUtil; import top.flya.common.exception.ServiceException; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurerSupport; import org.springframework.scheduling.annotation.EnableAsync; import java.util.Arrays; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; /** * 异步配置 * * @author Lion Li */ @EnableAsync(proxyTargetClass = true) @Configuration public class AsyncConfig extends AsyncConfigurerSupport { @Autowired @Qualifier("scheduledExecutorService") private ScheduledExecutorService scheduledExecutorService; /** * 自定义 @Async 注解使用系统线程池 */ @Override public Executor getAsyncExecutor() { return scheduledExecutorService; } /** * 异步执行异常处理 */ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (throwable, method, objects) -> { throwable.printStackTrace(); StringBuilder sb = new StringBuilder(); sb.append("Exception message - ").append(throwable.getMessage()) .append(", Method name - ").append(method.getName()); if (ArrayUtil.isNotEmpty(objects)) { sb.append(", Parameter value - ").append(Arrays.toString(objects)); } throw new ServiceException(sb.toString()); }; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/CaptchaConfig.java ================================================ package top.flya.framework.config; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.CircleCaptcha; import cn.hutool.captcha.LineCaptcha; import cn.hutool.captcha.ShearCaptcha; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import java.awt.*; /** * 验证码配置 * * @author Lion Li */ @Configuration public class CaptchaConfig { private static final int WIDTH = 160; private static final int HEIGHT = 60; private static final Color BACKGROUND = Color.PINK; private static final Font FONT = new Font("Arial", Font.BOLD, 48); /** * 圆圈干扰验证码 */ @Lazy @Bean public CircleCaptcha circleCaptcha() { CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT); captcha.setBackground(BACKGROUND); captcha.setFont(FONT); return captcha; } /** * 线段干扰的验证码 */ @Lazy @Bean public LineCaptcha lineCaptcha() { LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT); captcha.setBackground(BACKGROUND); captcha.setFont(FONT); return captcha; } /** * 扭曲干扰验证码 */ @Lazy @Bean public ShearCaptcha shearCaptcha() { ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT); captcha.setBackground(BACKGROUND); captcha.setFont(FONT); return captcha; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/EncryptorConfig.java ================================================ package top.flya.framework.config; import top.flya.framework.config.properties.EncryptorProperties; import top.flya.framework.manager.EncryptorManager; import top.flya.framework.encrypt.MybatisDecryptInterceptor; import top.flya.framework.encrypt.MybatisEncryptInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 加解密配置 * * @author 老马 * @version 4.6.0 */ @Configuration @ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true") public class EncryptorConfig { @Autowired private EncryptorProperties properties; @Bean public EncryptorManager encryptorManager() { return new EncryptorManager(); } @Bean public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) { return new MybatisEncryptInterceptor(encryptorManager, properties); } @Bean public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) { return new MybatisDecryptInterceptor(encryptorManager, properties); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/FilterConfig.java ================================================ package top.flya.framework.config; import top.flya.common.filter.RepeatableFilter; import top.flya.common.filter.XssFilter; import top.flya.common.utils.StringUtils; import top.flya.framework.config.properties.XssProperties; import org.springframework.beans.factory.annotation.Autowired; 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 javax.servlet.DispatcherType; import java.util.HashMap; import java.util.Map; /** * Filter配置 * * @author Lion Li */ @Configuration public class FilterConfig { @Autowired private XssProperties xssProperties; @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(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR)); registration.setName("xssFilter"); registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); Map initParameters = new HashMap(); initParameters.put("excludes", xssProperties.getExcludes()); 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; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/I18nConfig.java ================================================ package top.flya.framework.config; import cn.hutool.core.util.StrUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale; /** * 国际化配置 * * @author Lion Li */ @Configuration public class I18nConfig { @Bean public LocaleResolver localeResolver() { return new I18nLocaleResolver(); } /** * 获取请求头国际化信息 */ static class I18nLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { String language = httpServletRequest.getHeader("content-language"); Locale locale = Locale.getDefault(); if (StrUtil.isNotBlank(language)) { String[] split = language.split("_"); locale = new Locale(split[0], split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/JacksonConfig.java ================================================ package top.flya.framework.config; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import top.flya.framework.jackson.BigNumberSerializer; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.TimeZone; /** * jackson 配置 * * @author Lion Li */ @Slf4j @Configuration public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer customizer() { return builder -> { // 全局配置序列化返回 JSON 处理 JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE); javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE); javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE); javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); builder.modules(javaTimeModule); builder.timeZone(TimeZone.getDefault()); log.info("初始化 jackson 配置"); }; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/MailConfig.java ================================================ package top.flya.framework.config; import cn.hutool.extra.mail.MailAccount; import top.flya.framework.config.properties.MailProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * JavaMail 配置 * * @author Michelle.Chung */ @Configuration public class MailConfig { @Bean @ConditionalOnProperty(value = "mail.enabled", havingValue = "true") public MailAccount mailAccount(MailProperties mailProperties) { MailAccount account = new MailAccount(); account.setHost(mailProperties.getHost()); account.setPort(mailProperties.getPort()); account.setAuth(mailProperties.getAuth()); account.setFrom(mailProperties.getFrom()); account.setUser(mailProperties.getUser()); account.setPass(mailProperties.getPass()); account.setSocketFactoryPort(mailProperties.getPort()); account.setStarttlsEnable(mailProperties.getStarttlsEnable()); account.setSslEnable(mailProperties.getSslEnable()); account.setTimeout(mailProperties.getTimeout()); account.setConnectionTimeout(mailProperties.getConnectionTimeout()); return account; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/MybatisPlusConfig.java ================================================ package top.flya.framework.config; import cn.hutool.core.net.NetUtil; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import top.flya.framework.handler.CreateAndUpdateMetaObjectHandler; import top.flya.framework.interceptor.PlusDataPermissionInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * mybatis-plus配置类(下方注释有插件介绍) * * @author Lion Li */ @EnableTransactionManagement(proxyTargetClass = true) @Configuration @MapperScan("${mybatis-plus.mapperPackage}") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 数据权限处理 interceptor.addInnerInterceptor(dataPermissionInterceptor()); // 分页插件 interceptor.addInnerInterceptor(paginationInnerInterceptor()); // 乐观锁插件 interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); return interceptor; } /** * 数据权限拦截器 */ public PlusDataPermissionInterceptor dataPermissionInterceptor() { return new PlusDataPermissionInterceptor(); } /** * 分页插件,自动识别数据库类型 */ public PaginationInnerInterceptor paginationInnerInterceptor() { PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); // 设置最大单页限制数量,默认 500 条,-1 不受限制 paginationInnerInterceptor.setMaxLimit(-1L); // 分页合理化 paginationInnerInterceptor.setOverflow(true); return paginationInnerInterceptor; } /** * 乐观锁插件 */ public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { return new OptimisticLockerInnerInterceptor(); } /** * 元对象字段填充控制器 */ @Bean public MetaObjectHandler metaObjectHandler() { return new CreateAndUpdateMetaObjectHandler(); } /** * 使用网卡信息绑定雪花生成器 * 防止集群雪花ID重复 */ @Bean public IdentifierGenerator idGenerator() { return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); } /** * PaginationInnerInterceptor 分页插件,自动识别数据库类型 * https://baomidou.com/pages/97710a/ * OptimisticLockerInnerInterceptor 乐观锁插件 * https://baomidou.com/pages/0d93c0/ * MetaObjectHandler 元对象字段填充控制器 * https://baomidou.com/pages/4c6bcf/ * ISqlInjector sql注入器 * https://baomidou.com/pages/42ea4a/ * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作,就会终止该操作 * https://baomidou.com/pages/f9a237/ * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截) * IdentifierGenerator 自定义主键策略 * https://baomidou.com/pages/568eb2/ * TenantLineInnerInterceptor 多租户插件 * https://baomidou.com/pages/aef2f2/ * DynamicTableNameInnerInterceptor 动态表名插件 * https://baomidou.com/pages/2a45ff/ */ } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/RedisConfig.java ================================================ package top.flya.framework.config; import cn.hutool.core.util.ObjectUtil; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.data.redis.connection.MessageListener; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import top.flya.framework.config.properties.RedissonProperties; import top.flya.framework.handler.KeyPrefixHandler; import top.flya.framework.manager.PlusSpringCacheManager; import lombok.extern.slf4j.Slf4j; import org.redisson.codec.JsonJacksonCodec; import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * redis配置 * * @author Lion Li */ @Slf4j @Configuration @EnableCaching @EnableConfigurationProperties(RedissonProperties.class) public class RedisConfig { @Autowired private RedissonProperties redissonProperties; @Autowired private ObjectMapper objectMapper; @Bean public RedissonAutoConfigurationCustomizer redissonCustomizer() { return config -> { config.setThreads(redissonProperties.getThreads()) .setNettyThreads(redissonProperties.getNettyThreads()) .setCodec(new JsonJacksonCodec(objectMapper)); RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); if (ObjectUtil.isNotNull(singleServerConfig)) { // 使用单机模式 config.useSingleServer() //设置redis key前缀 .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix())) .setTimeout(singleServerConfig.getTimeout()) .setClientName(singleServerConfig.getClientName()) .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()); } // 集群配置方式 参考下方注释 RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig(); if (ObjectUtil.isNotNull(clusterServersConfig)) { config.useClusterServers() //设置redis key前缀 .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix())) .setTimeout(clusterServersConfig.getTimeout()) .setClientName(clusterServersConfig.getClientName()) .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize()) .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) .setReadMode(clusterServersConfig.getReadMode()) .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()); } log.info("初始化 redis 配置"); }; } /** * 自定义缓存管理器 整合spring-cache */ @Bean public CacheManager cacheManager() { return new PlusSpringCacheManager(); } /** * redis集群配置 yml * * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉) * spring: * redis: * cluster: * nodes: * - 192.168.0.100:6379 * - 192.168.0.101:6379 * - 192.168.0.102:6379 * # 密码 * password: * # 连接超时时间 * timeout: 10s * # 是否开启ssl * ssl: false * * redisson: * # 线程池数量 * threads: 16 * # Netty线程池数量 * nettyThreads: 32 * # 集群配置 * clusterServersConfig: * # 客户端名称 * clientName: ${ruoyi.name} * # master最小空闲连接数 * masterConnectionMinimumIdleSize: 32 * # master连接池大小 * masterConnectionPoolSize: 64 * # slave最小空闲连接数 * slaveConnectionMinimumIdleSize: 32 * # slave连接池大小 * slaveConnectionPoolSize: 64 * # 连接空闲超时,单位:毫秒 * idleConnectionTimeout: 10000 * # 命令等待超时,单位:毫秒 * timeout: 3000 * # 发布和订阅连接池大小 * subscriptionConnectionPoolSize: 50 * # 读取模式 * readMode: "SLAVE" * # 订阅模式 * subscriptionMode: "MASTER" */ } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/ResourcesConfig.java ================================================ package top.flya.framework.config; import top.flya.framework.interceptor.PlusWebInvokeTimeInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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; /** * 通用配置 * * @author Lion Li */ @Configuration public class ResourcesConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 全局访问性能拦截 registry.addInterceptor(new PlusWebInvokeTimeInterceptor()); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { } /** * 跨域配置 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 设置访问源地址 config.addAllowedOriginPattern("*"); // 设置访问源请求头 config.addAllowedHeader("*"); // 设置访问源请求方法 config.addAllowedMethod("*"); // 有效期 1800秒 config.setMaxAge(1800L); // 添加映射路径,拦截一切请求 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); // 返回新的CorsFilter return new CorsFilter(source); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/SaTokenConfig.java ================================================ package top.flya.framework.config; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.interceptor.SaInterceptor; import cn.dev33.satoken.jwt.StpLogicJwtForSimple; import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpUtil; import top.flya.common.utils.spring.SpringUtils; import top.flya.framework.config.properties.SecurityProperties; import top.flya.framework.handler.AllUrlHandler; import top.flya.framework.satoken.dao.PlusSaTokenDao; import top.flya.framework.satoken.service.SaPermissionImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * sa-token 配置 * * @author Lion Li */ @RequiredArgsConstructor @Slf4j @Configuration public class SaTokenConfig implements WebMvcConfigurer { private final SecurityProperties securityProperties; /** * 注册sa-token的拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 注册路由拦截器,自定义验证规则 registry.addInterceptor(new SaInterceptor(handler -> { AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class); // 登录验证 -- 排除多个路径 SaRouter // 获取所有的 .match(allUrlHandler.getUrls()) // 对未排除的路径进行检查 .check(() -> { // 检查是否登录 是否有token StpUtil.checkLogin(); // 有效率影响 用于临时测试 // if (log.isDebugEnabled()) { // log.debug("剩余有效时间: {}", StpUtil.getTokenTimeout()); // log.debug("临时有效时间: {}", StpUtil.getTokenActivityTimeout()); // } }); })).addPathPatterns("/**") // 排除不需要拦截的路径 .excludePathPatterns(securityProperties.getExcludes()); } @Bean public StpLogic getStpLogicJwt() { // Sa-Token 整合 jwt (简单模式) return new StpLogicJwtForSimple(); } /** * 权限接口实现(使用bean注入方便用户替换) */ @Bean public StpInterface stpInterface() { return new SaPermissionImpl(); } /** * 自定义dao层存储 */ @Bean public SaTokenDao saTokenDao() { return new PlusSaTokenDao(); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/SwaggerConfig.java ================================================ package top.flya.framework.config; import top.flya.common.utils.StringUtils; import top.flya.framework.config.properties.SwaggerProperties; import top.flya.framework.handler.OpenApiHandler; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityRequirement; import lombok.RequiredArgsConstructor; import org.springdoc.core.*; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.providers.JavadocProvider; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; /** * Swagger 文档配置 * * @author Lion Li */ @RequiredArgsConstructor @Configuration @AutoConfigureBefore(SpringDocConfiguration.class) @ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true) public class SwaggerConfig { private final SwaggerProperties swaggerProperties; private final ServerProperties serverProperties; @Bean @ConditionalOnMissingBean(OpenAPI.class) public OpenAPI openApi() { OpenAPI openApi = new OpenAPI(); // 文档基本信息 SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo(); Info info = convertInfo(infoProperties); openApi.info(info); // 扩展文档信息 openApi.externalDocs(swaggerProperties.getExternalDocs()); openApi.tags(swaggerProperties.getTags()); openApi.paths(swaggerProperties.getPaths()); openApi.components(swaggerProperties.getComponents()); Set keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet(); List list = new ArrayList<>(); SecurityRequirement securityRequirement = new SecurityRequirement(); keySet.forEach(securityRequirement::addList); list.add(securityRequirement); openApi.security(list); return openApi; } private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) { Info info = new Info(); info.setTitle(infoProperties.getTitle()); info.setDescription(infoProperties.getDescription()); info.setContact(infoProperties.getContact()); info.setLicense(infoProperties.getLicense()); info.setVersion(infoProperties.getVersion()); return info; } /** * 自定义 openapi 处理器 */ @Bean public OpenAPIService openApiBuilder(Optional openAPI, SecurityService securityParser, SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, Optional> openApiBuilderCustomisers, Optional> serverBaseUrlCustomisers, Optional javadocProvider) { return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider); } /** * 对已经生成好的 OpenApi 进行自定义操作 */ @Bean public OpenApiCustomiser openApiCustomiser() { String contextPath = serverProperties.getServlet().getContextPath(); String finalContextPath; if (StringUtils.isBlank(contextPath) || "/".equals(contextPath)) { finalContextPath = ""; } else { finalContextPath = contextPath; } // 对所有路径增加前置上下文路径 return openApi -> { Paths oldPaths = openApi.getPaths(); if (oldPaths instanceof PlusPaths) { return; } PlusPaths newPaths = new PlusPaths(); oldPaths.forEach((k,v) -> newPaths.addPathItem(finalContextPath + k, v)); openApi.setPaths(newPaths); }; } /** * 单独使用一个类便于判断 解决springdoc路径拼接重复问题 * * @author Lion Li */ static class PlusPaths extends Paths { public PlusPaths() { super(); } } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/ThreadPoolConfig.java ================================================ package top.flya.framework.config; import top.flya.common.utils.Threads; import top.flya.framework.config.properties.ThreadPoolProperties; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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 Lion Li **/ @Configuration public class ThreadPoolConfig { /** * 核心线程数 = cpu 核心数 + 1 */ private final int core = Runtime.getRuntime().availableProcessors() + 1; @Autowired private ThreadPoolProperties threadPoolProperties; @Bean(name = "threadPoolTaskExecutor") @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(core); executor.setMaxPoolSize(core * 2); executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } /** * 执行周期性或定时任务 */ @Bean(name = "scheduledExecutorService") protected ScheduledExecutorService scheduledExecutorService() { return new ScheduledThreadPoolExecutor(core, 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); } }; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/TranslationConfig.java ================================================ package top.flya.framework.config; import com.fasterxml.jackson.databind.ObjectMapper; import top.flya.common.annotation.TranslationType; import top.flya.common.translation.TranslationInterface; import top.flya.common.translation.handler.TranslationBeanSerializerModifier; import top.flya.common.translation.handler.TranslationHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 翻译模块配置类 * * @author Lion Li */ @Slf4j @Configuration public class TranslationConfig { @Autowired private List> list; @Autowired private ObjectMapper objectMapper; @PostConstruct public void init() { Map> map = new HashMap<>(list.size()); for (TranslationInterface trans : list) { if (trans.getClass().isAnnotationPresent(TranslationType.class)) { TranslationType annotation = trans.getClass().getAnnotation(TranslationType.class); map.put(annotation.type(), trans); } else { log.warn(trans.getClass().getName() + " 翻译实现类未标注 TranslationType 注解!"); } } TranslationHandler.TRANSLATION_MAPPER.putAll(map); // 设置 Bean 序列化修改器 objectMapper.setSerializerFactory( objectMapper.getSerializerFactory() .withSerializerModifier(new TranslationBeanSerializerModifier())); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/UndertowConfig.java ================================================ package top.flya.framework.config; import io.undertow.server.DefaultByteBufferPool; import io.undertow.websockets.jsr.WebSocketDeploymentInfo; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Configuration; /** * Undertow 自定义配置 * * @author Lion Li */ @Configuration public class UndertowConfig implements WebServerFactoryCustomizer { /** * 设置 Undertow 的 websocket 缓冲池 */ @Override public void customize(UndertowServletWebServerFactory factory) { // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配 factory.addDeploymentInfoCustomizers(deploymentInfo -> { WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512)); deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); }); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/ValidatorConfig.java ================================================ package top.flya.framework.config; import org.hibernate.validator.HibernateValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import javax.validation.Validator; import java.util.Properties; /** * 校验框架配置类 * * @author Lion Li */ @Configuration public class ValidatorConfig { @Autowired private MessageSource messageSource; /** * 配置校验框架 快速返回模式 */ @Bean public Validator validator() { LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); // 国际化 factoryBean.setValidationMessageSource(messageSource); // 设置使用 HibernateValidator 校验器 factoryBean.setProviderClass(HibernateValidator.class); Properties properties = new Properties(); // 设置 快速异常返回 properties.setProperty("hibernate.validator.fail_fast", "true"); factoryBean.setValidationProperties(properties); // 加载配置 factoryBean.afterPropertiesSet(); return factoryBean.getValidator(); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/CaptchaProperties.java ================================================ package top.flya.framework.config.properties; import top.flya.common.enums.CaptchaCategory; import top.flya.common.enums.CaptchaType; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 验证码 配置属性 * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "captcha") public class CaptchaProperties { /** * 验证码类型 */ private CaptchaType type; /** * 验证码类别 */ private CaptchaCategory category; /** * 数字验证码位数 */ private Integer numberLength; /** * 字符验证码长度 */ private Integer charLength; } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/EncryptorProperties.java ================================================ package top.flya.framework.config.properties; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 加解密属性配置类 * * @author 老马 * @version 4.6.0 */ @Data @Component @ConfigurationProperties(prefix = "mybatis-encryptor") public class EncryptorProperties { /** * 过滤开关 */ private Boolean enable; /** * 默认算法 */ private AlgorithmType algorithm; /** * 安全秘钥 */ private String password; /** * 公钥 */ private String publicKey; /** * 私钥 */ private String privateKey; /** * 编码方式,base64/hex */ private EncodeType encode; } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/MailProperties.java ================================================ package top.flya.framework.config.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * JavaMail 配置属性 * * @author Michelle.Chung */ @Data @Component @ConfigurationProperties(prefix = "mail") public class MailProperties { /** * 过滤开关 */ private Boolean enabled; /** * SMTP服务器域名 */ private String host; /** * SMTP服务端口 */ private Integer port; /** * 是否需要用户名密码验证 */ private Boolean auth; /** * 用户名 */ private String user; /** * 密码 */ private String pass; /** * 发送方,遵循RFC-822标准 */ private String from; /** * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 */ private Boolean starttlsEnable; /** * 使用 SSL安全连接 */ private Boolean sslEnable; /** * SMTP超时时长,单位毫秒,缺省值不超时 */ private Long timeout; /** * Socket连接超时值,单位毫秒,缺省值不超时 */ private Long connectionTimeout; } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/RedissonProperties.java ================================================ package top.flya.framework.config.properties; import lombok.Data; import lombok.NoArgsConstructor; import org.redisson.config.ReadMode; import org.redisson.config.SubscriptionMode; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * Redisson 配置属性 * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "redisson") public class RedissonProperties { /** * redis缓存key前缀 */ private String keyPrefix; /** * 线程池数量,默认值 = 当前处理核数量 * 2 */ private int threads; /** * Netty线程池数量,默认值 = 当前处理核数量 * 2 */ private int nettyThreads; /** * 单机服务配置 */ private SingleServerConfig singleServerConfig; /** * 集群服务配置 */ private ClusterServersConfig clusterServersConfig; @Data @NoArgsConstructor public static class SingleServerConfig { /** * 客户端名称 */ private String clientName; /** * 最小空闲连接数 */ private int connectionMinimumIdleSize; /** * 连接池大小 */ private int connectionPoolSize; /** * 连接空闲超时,单位:毫秒 */ private int idleConnectionTimeout; /** * 命令等待超时,单位:毫秒 */ private int timeout; /** * 发布和订阅连接池大小 */ private int subscriptionConnectionPoolSize; } @Data @NoArgsConstructor public static class ClusterServersConfig { /** * 客户端名称 */ private String clientName; /** * master最小空闲连接数 */ private int masterConnectionMinimumIdleSize; /** * master连接池大小 */ private int masterConnectionPoolSize; /** * slave最小空闲连接数 */ private int slaveConnectionMinimumIdleSize; /** * slave连接池大小 */ private int slaveConnectionPoolSize; /** * 连接空闲超时,单位:毫秒 */ private int idleConnectionTimeout; /** * 命令等待超时,单位:毫秒 */ private int timeout; /** * 发布和订阅连接池大小 */ private int subscriptionConnectionPoolSize; /** * 读取模式 */ private ReadMode readMode; /** * 订阅模式 */ private SubscriptionMode subscriptionMode; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/SecurityProperties.java ================================================ package top.flya.framework.config.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * Security 配置属性 * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "security") public class SecurityProperties { /** * 排除路径 */ private String[] excludes; } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/SwaggerProperties.java ================================================ package top.flya.framework.config.properties; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.tags.Tag; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.stereotype.Component; import java.util.List; /** * swagger 配置属性 * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "swagger") public class SwaggerProperties { /** * 文档基本信息 */ @NestedConfigurationProperty private InfoProperties info = new InfoProperties(); /** * 扩展文档地址 */ @NestedConfigurationProperty private ExternalDocumentation externalDocs; /** * 标签 */ private List tags = null; /** * 路径 */ @NestedConfigurationProperty private Paths paths = null; /** * 组件 */ @NestedConfigurationProperty private Components components = null; /** *

        * 文档的基础属性信息 *

        * * @see io.swagger.v3.oas.models.info.Info * * 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来 */ @Data public static class InfoProperties { /** * 标题 */ private String title = null; /** * 描述 */ private String description = null; /** * 联系人信息 */ @NestedConfigurationProperty private Contact contact = null; /** * 许可证 */ @NestedConfigurationProperty private License license = null; /** * 版本 */ private String version = null; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/ThreadPoolProperties.java ================================================ package top.flya.framework.config.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 线程池 配置属性 * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "thread-pool") public class ThreadPoolProperties { /** * 是否开启线程池 */ private boolean enabled; /** * 队列最大长度 */ private int queueCapacity; /** * 线程池维护线程所允许的空闲时间 */ private int keepAliveSeconds; } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/config/properties/XssProperties.java ================================================ package top.flya.framework.config.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * xss过滤 配置属性 * * @author Lion Li */ @Data @Component @ConfigurationProperties(prefix = "xss") public class XssProperties { /** * 过滤开关 */ private String enabled; /** * 排除链接(多个用逗号分隔) */ private String excludes; /** * 匹配链接 */ private String urlPatterns; } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/encrypt/MybatisDecryptInterceptor.java ================================================ package top.flya.framework.encrypt; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import top.flya.common.annotation.EncryptField; import top.flya.common.encrypt.EncryptContext; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import top.flya.common.utils.StringUtils; import top.flya.framework.config.properties.EncryptorProperties; import top.flya.framework.manager.EncryptorManager; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.plugin.*; import java.lang.reflect.Field; import java.sql.Statement; import java.util.*; /** * 出参解密拦截器 * * @author 老马 * @version 4.6.0 */ @Slf4j @Intercepts({@Signature( type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}) }) @AllArgsConstructor public class MybatisDecryptInterceptor implements Interceptor { private final EncryptorManager encryptorManager; private final EncryptorProperties defaultProperties; @Override public Object intercept(Invocation invocation) throws Throwable { // 获取执行mysql执行结果 Object result = invocation.proceed(); if (result == null) { return null; } decryptHandler(result); return result; } /** * 解密对象 * * @param sourceObject 待加密对象 */ private void decryptHandler(Object sourceObject) { if (ObjectUtil.isNull(sourceObject)) { return; } if (sourceObject instanceof Map) { new HashSet<>(((Map) sourceObject).values()).forEach(this::decryptHandler); return; } if (sourceObject instanceof List) { List sourceList = (List) sourceObject; if(CollUtil.isEmpty(sourceList)) { return; } // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 Object firstItem = sourceList.get(0); if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { return; } ((List) sourceObject).forEach(this::decryptHandler); return; } Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); try { for (Field field : fields) { field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field)); } } catch (Exception e) { log.error("处理解密字段时出错", e); } } /** * 字段值进行加密。通过字段的批注注册新的加密算法 * * @param value 待加密的值 * @param field 待加密字段 * @return 加密后结果 */ private String decryptField(String value, Field field) { if (ObjectUtil.isNull(value)) { return null; } EncryptField encryptField = field.getAnnotation(EncryptField.class); EncryptContext encryptContext = new EncryptContext(); encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); return this.encryptorManager.decrypt(value, encryptContext); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/encrypt/MybatisEncryptInterceptor.java ================================================ package top.flya.framework.encrypt; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import top.flya.common.annotation.EncryptField; import top.flya.common.encrypt.EncryptContext; import top.flya.common.enums.AlgorithmType; import top.flya.common.enums.EncodeType; import top.flya.common.utils.StringUtils; import top.flya.framework.config.properties.EncryptorProperties; import top.flya.framework.manager.EncryptorManager; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Signature; import java.lang.reflect.Field; import java.sql.PreparedStatement; import java.util.*; /** * 入参加密拦截器 * * @author 老马 * @version 4.6.0 */ @Slf4j @Intercepts({@Signature( type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}) }) @AllArgsConstructor public class MybatisEncryptInterceptor implements Interceptor { private final EncryptorManager encryptorManager; private final EncryptorProperties defaultProperties; @Override public Object intercept(Invocation invocation) throws Throwable { return invocation; } @Override public Object plugin(Object target) { if (target instanceof ParameterHandler) { // 进行加密操作 ParameterHandler parameterHandler = (ParameterHandler) target; Object parameterObject = parameterHandler.getParameterObject(); if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) { this.encryptHandler(parameterObject); } } return target; } /** * 加密对象 * * @param sourceObject 待加密对象 */ private void encryptHandler(Object sourceObject) { if (ObjectUtil.isNull(sourceObject)) { return; } if (sourceObject instanceof Map) { new HashSet<>(((Map) sourceObject).values()).forEach(this::encryptHandler); return; } if (sourceObject instanceof List) { List sourceList = (List) sourceObject; if(CollUtil.isEmpty(sourceList)) { return; } // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 Object firstItem = sourceList.get(0); if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { return; } ((List) sourceObject).forEach(this::encryptHandler); return; } Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); try { for (Field field : fields) { field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field)); } } catch (Exception e) { log.error("处理加密字段时出错", e); } } /** * 字段值进行加密。通过字段的批注注册新的加密算法 * * @param value 待加密的值 * @param field 待加密字段 * @return 加密后结果 */ private String encryptField(String value, Field field) { if (ObjectUtil.isNull(value)) { return null; } EncryptField encryptField = field.getAnnotation(EncryptField.class); EncryptContext encryptContext = new EncryptContext(); encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); return this.encryptorManager.encrypt(value, encryptContext); } @Override public void setProperties(Properties properties) { } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/handler/AllUrlHandler.java ================================================ package top.flya.framework.handler; import cn.hutool.core.util.ReUtil; import top.flya.common.utils.spring.SpringUtils; import lombok.Data; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.util.*; import java.util.regex.Pattern; /** * 获取所有Url配置 * * @author Lion Li */ @Data @Component public class AllUrlHandler implements InitializingBean { private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); private List urls = new ArrayList<>(); @Override public void afterPropertiesSet() { Set set = new HashSet<>(); RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); Map map = mapping.getHandlerMethods(); map.keySet().forEach(info -> { // 获取注解上边的 path 替代 path variable 为 * Objects.requireNonNull(info.getPathPatternsCondition().getPatterns()) .forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*"))); }); urls.addAll(set); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/handler/CreateAndUpdateMetaObjectHandler.java ================================================ package top.flya.framework.handler; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpStatus; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.exception.ServiceException; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StringUtils; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import java.util.Date; /** * MP注入处理器 * * @author Lion Li * @date 2021/4/25 */ @Slf4j public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { try { if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) { BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject(); Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime()) ? baseEntity.getCreateTime() : new Date(); baseEntity.setCreateTime(current); baseEntity.setUpdateTime(current); // String username =getLoginUsername(); // StringUtils.isNotBlank(baseEntity.getCreateBy()) ? baseEntity.getCreateBy() : // // 当前已登录 且 创建人为空 则填充 // baseEntity.setCreateBy(username); // // 当前已登录 且 更新人为空 则填充 // baseEntity.setUpdateBy(username); } } catch (Exception e) { throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); } } @Override public void updateFill(MetaObject metaObject) { try { if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) { BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject(); Date current = new Date(); // 更新时间填充(不管为不为空) baseEntity.setUpdateTime(current); // String username = getLoginUsername(); // 当前已登录 更新人填充(不管为不为空) // if (StringUtils.isNotBlank(username)) { // baseEntity.setUpdateBy(username); // } } } catch (Exception e) { throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); } } /** * 获取登录用户名 */ private String getLoginUsername() { LoginUser loginUser; try { loginUser = LoginHelper.getLoginUser(); } catch (Exception e) { log.warn("自动注入警告 => 用户未登录"); return null; } return ObjectUtil.isNotNull(loginUser) ? loginUser.getUsername() : null; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/handler/KeyPrefixHandler.java ================================================ package top.flya.framework.handler; import top.flya.common.utils.StringUtils; import org.redisson.api.NameMapper; /** * redis缓存key前缀处理 * * @author ye * @date 2022/7/14 17:44 * @since 4.3.0 */ public class KeyPrefixHandler implements NameMapper { private final String keyPrefix; public KeyPrefixHandler(String keyPrefix) { //前缀为空 则返回空前缀 this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":"; } /** * 增加前缀 */ @Override public String map(String name) { if (StringUtils.isBlank(name)) { return null; } if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) { return keyPrefix + name; } return name; } /** * 去除前缀 */ @Override public String unmap(String name) { if (StringUtils.isBlank(name)) { return null; } if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) { return name.substring(keyPrefix.length()); } return name; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/handler/OpenApiHandler.java ================================================ package top.flya.framework.handler; import cn.hutool.core.io.IoUtil; import io.swagger.v3.core.jackson.TypeNameResolver; import io.swagger.v3.core.util.AnnotationsUtils; import io.swagger.v3.oas.annotations.tags.Tags; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.tags.Tag; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springdoc.core.OpenAPIService; import org.springdoc.core.PropertyResolverUtils; import org.springdoc.core.SecurityService; import org.springdoc.core.SpringDocConfigProperties; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.providers.JavadocProvider; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.CollectionUtils; import org.springframework.web.method.HandlerMethod; import java.io.StringReader; import java.lang.reflect.Method; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 自定义 openapi 处理器 * 对源码功能进行修改 增强使用 */ @SuppressWarnings("all") public class OpenApiHandler extends OpenAPIService { /** * The constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIService.class); /** * The Context. */ private ApplicationContext context; /** * The Security parser. */ private final SecurityService securityParser; /** * The Mappings map. */ private final Map mappingsMap = new HashMap<>(); /** * The Springdoc tags. */ private final Map springdocTags = new HashMap<>(); /** * The Open api builder customisers. */ private final Optional> openApiBuilderCustomisers; /** * The server base URL customisers. */ private final Optional> serverBaseUrlCustomizers; /** * The Spring doc config properties. */ private final SpringDocConfigProperties springDocConfigProperties; /** * The Open api. */ private OpenAPI openAPI; /** * The Cached open api map. */ private final Map cachedOpenAPI = new HashMap<>(); /** * The Is servers present. */ private boolean isServersPresent; /** * The Server base url. */ private String serverBaseUrl; /** * The Property resolver utils. */ private final PropertyResolverUtils propertyResolverUtils; /** * The javadoc provider. */ private final Optional javadocProvider; /** * The Basic error controller. */ private static Class basicErrorController; static { try { //spring-boot 2 basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController"); } catch (ClassNotFoundException e) { //spring-boot 1 try { basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.BasicErrorController"); } catch (ClassNotFoundException classNotFoundException) { //Basic error controller class not found LOGGER.trace(classNotFoundException.getMessage()); } } } /** * Instantiates a new Open api builder. * * @param openAPI the open api * @param securityParser the security parser * @param springDocConfigProperties the spring doc config properties * @param propertyResolverUtils the property resolver utils * @param openApiBuilderCustomizers the open api builder customisers * @param serverBaseUrlCustomizers the server base url customizers * @param javadocProvider the javadoc provider */ public OpenApiHandler(Optional openAPI, SecurityService securityParser, SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, Optional> openApiBuilderCustomizers, Optional> serverBaseUrlCustomizers, Optional javadocProvider) { super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); if (openAPI.isPresent()) { this.openAPI = openAPI.get(); if (this.openAPI.getComponents() == null) this.openAPI.setComponents(new Components()); if (this.openAPI.getPaths() == null) this.openAPI.setPaths(new Paths()); if (!CollectionUtils.isEmpty(this.openAPI.getServers())) this.isServersPresent = true; } this.propertyResolverUtils = propertyResolverUtils; this.securityParser = securityParser; this.springDocConfigProperties = springDocConfigProperties; this.openApiBuilderCustomisers = openApiBuilderCustomizers; this.serverBaseUrlCustomizers = serverBaseUrlCustomizers; this.javadocProvider = javadocProvider; if (springDocConfigProperties.isUseFqn()) TypeNameResolver.std.setUseFqn(true); } @Override public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) { Set tags = new HashSet<>(); Set tagsStr = new HashSet<>(); buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale); buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale); if (!CollectionUtils.isEmpty(tagsStr)) tagsStr = tagsStr.stream() .map(str -> propertyResolverUtils.resolve(str, locale)) .collect(Collectors.toSet()); if (springdocTags.containsKey(handlerMethod)) { io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod); tagsStr.add(tag.getName()); if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { openAPI.addTagsItem(tag); } } if (!CollectionUtils.isEmpty(tagsStr)) { if (CollectionUtils.isEmpty(operation.getTags())) operation.setTags(new ArrayList<>(tagsStr)); else { Set operationTagsSet = new HashSet<>(operation.getTags()); operationTagsSet.addAll(tagsStr); operation.getTags().clear(); operation.getTags().addAll(operationTagsSet); } } if (isAutoTagClasses(operation)) { if (javadocProvider.isPresent()) { String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType()); if (StringUtils.isNotBlank(description)) { io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); // 自定义部分 修改使用java注释当tag名 List list = IoUtil.readLines(new StringReader(description), new ArrayList<>()); // tag.setName(tagAutoName); tag.setName(list.get(0)); operation.addTagsItem(list.get(0)); tag.setDescription(description); if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { openAPI.addTagsItem(tag); } } } else { String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName()); operation.addTagsItem(tagAutoName); } } if (!CollectionUtils.isEmpty(tags)) { // Existing tags List openApiTags = openAPI.getTags(); if (!CollectionUtils.isEmpty(openApiTags)) tags.addAll(openApiTags); openAPI.setTags(new ArrayList<>(tags)); } // Handle SecurityRequirement at operation level io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser .getSecurityRequirements(handlerMethod); if (securityRequirements != null) { if (securityRequirements.length == 0) operation.setSecurity(Collections.emptyList()); else securityParser.buildSecurityRequirement(securityRequirements, operation); } return operation; } private void buildTagsFromMethod(Method method, Set tags, Set tagsStr, Locale locale) { // method tags Set tagsSet = AnnotatedElementUtils .findAllMergedAnnotations(method, Tags.class); Set methodTags = tagsSet.stream() .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); if (!CollectionUtils.isEmpty(methodTags)) { tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet())); List allTags = new ArrayList<>(methodTags); addTags(allTags, tags, locale); } } private void addTags(List sourceTags, Set tags, Locale locale) { Optional> optionalTagSet = AnnotationsUtils .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true); optionalTagSet.ifPresent(tagsSet -> { tagsSet.forEach(tag -> { tag.name(propertyResolverUtils.resolve(tag.getName(), locale)); tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale)); if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) tags.add(tag); }); }); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/handler/PlusDataPermissionHandler.java ================================================ package top.flya.framework.handler; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ConcurrentHashSet; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; import top.flya.common.annotation.DataColumn; import top.flya.common.annotation.DataPermission; import top.flya.common.core.domain.dto.RoleDTO; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.enums.DataScopeType; import top.flya.common.exception.ServiceException; import top.flya.common.helper.DataPermissionHelper; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Parenthesis; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.BeanResolver; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; /** * 数据权限过滤 * * @author Lion Li * @version 3.5.0 */ @Slf4j public class PlusDataPermissionHandler { /** * 方法或类(名称) 与 注解的映射关系缓存 */ private final Map dataPermissionCacheMap = new ConcurrentHashMap<>(); /** * 无效注解方法缓存用于快速返回 */ private final Set invalidCacheSet = new ConcurrentHashSet<>(); /** * spel 解析器 */ private final ExpressionParser parser = new SpelExpressionParser(); private final ParserContext parserContext = new TemplateParserContext(); /** * bean解析器 用于处理 spel 表达式中对 bean 的调用 */ private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { DataColumn[] dataColumns = findAnnotation(mappedStatementId); if (ArrayUtil.isEmpty(dataColumns)) { invalidCacheSet.add(mappedStatementId); return where; } LoginUser currentUser = DataPermissionHelper.getVariable("user"); if (ObjectUtil.isNull(currentUser)) { currentUser = LoginHelper.getLoginUser(); DataPermissionHelper.setVariable("user", currentUser); } // 如果是超级管理员,则不过滤数据 if (LoginHelper.isAdmin()) { return where; } String dataFilterSql = buildDataFilter(dataColumns, isSelect); if (StringUtils.isBlank(dataFilterSql)) { return where; } try { Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql); // 数据权限使用单独的括号 防止与其他条件冲突 Parenthesis parenthesis = new Parenthesis(expression); if (ObjectUtil.isNotNull(where)) { return new AndExpression(where, parenthesis); } else { return parenthesis; } } catch (JSQLParserException e) { throw new ServiceException("数据权限解析异常 => " + e.getMessage()); } } /** * 构造数据过滤sql */ private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) { // 更新或删除需满足所有条件 String joinStr = isSelect ? " OR " : " AND "; LoginUser user = DataPermissionHelper.getVariable("user"); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(beanResolver); DataPermissionHelper.getContext().forEach(context::setVariable); Set conditions = new HashSet<>(); for (RoleDTO role : user.getRoles()) { user.setRoleId(role.getRoleId()); // 获取角色权限泛型 DataScopeType type = DataScopeType.findCode(role.getDataScope()); if (ObjectUtil.isNull(type)) { throw new ServiceException("角色数据范围异常 => " + role.getDataScope()); } // 全部数据权限直接返回 if (type == DataScopeType.ALL) { return ""; } boolean isSuccess = false; for (DataColumn dataColumn : dataColumns) { if (dataColumn.key().length != dataColumn.value().length) { throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); } // 不包含 key 变量 则不处理 if (!StringUtils.containsAny(type.getSqlTemplate(), Arrays.stream(dataColumn.key()).map(key -> "#" + key).toArray(String[]::new) )) { continue; } // 设置注解变量 key 为表达式变量 value 为变量值 for (int i = 0; i < dataColumn.key().length; i++) { context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); } // 解析sql模板并填充 String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class); conditions.add(joinStr + sql); isSuccess = true; } // 未处理成功则填充兜底方案 if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) { conditions.add(joinStr + type.getElseSql()); } } if (CollUtil.isNotEmpty(conditions)) { String sql = StreamUtils.join(conditions, Function.identity(), ""); return sql.substring(joinStr.length()); } return ""; } private DataColumn[] findAnnotation(String mappedStatementId) { StringBuilder sb = new StringBuilder(mappedStatementId); int index = sb.lastIndexOf("."); String clazzName = sb.substring(0, index); String methodName = sb.substring(index + 1, sb.length()); Class clazz = ClassUtil.loadClass(clazzName); List methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz)) .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()); DataPermission dataPermission; // 获取方法注解 for (Method method : methods) { dataPermission = dataPermissionCacheMap.get(mappedStatementId); if (ObjectUtil.isNotNull(dataPermission)) { return dataPermission.value(); } if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) { dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class); dataPermissionCacheMap.put(mappedStatementId, dataPermission); return dataPermission.value(); } } dataPermission = dataPermissionCacheMap.get(clazz.getName()); if (ObjectUtil.isNotNull(dataPermission)) { return dataPermission.value(); } // 获取类注解 if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); dataPermissionCacheMap.put(clazz.getName(), dataPermission); return dataPermission.value(); } return null; } /** * 是否为无效方法 无数据权限 */ public boolean isInvalid(String mappedStatementId) { return invalidCacheSet.contains(mappedStatementId); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/interceptor/PlusDataPermissionInterceptor.java ================================================ package top.flya.framework.interceptor; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import com.baomidou.mybatisplus.core.toolkit.PluginUtils; import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import top.flya.framework.handler.PlusDataPermissionHandler; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.update.Update; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import java.sql.Connection; import java.sql.SQLException; import java.util.List; /** * 数据权限拦截器 * * @author Lion Li * @version 3.5.0 */ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { // 检查忽略注解 if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { return; } // 检查是否无效 无数据权限注解 if (dataPermissionHandler.isInvalid(ms.getId())) { return; } // 解析 sql 分配对应方法 PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); } @Override public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); MappedStatement ms = mpSh.mappedStatement(); SqlCommandType sct = ms.getSqlCommandType(); if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { return; } PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); } } @Override protected void processSelect(Select select, int index, String sql, Object obj) { SelectBody selectBody = select.getSelectBody(); if (selectBody instanceof PlainSelect) { this.setWhere((PlainSelect) selectBody, (String) obj); } else if (selectBody instanceof SetOperationList) { SetOperationList setOperationList = (SetOperationList) selectBody; List selectBodyList = setOperationList.getSelects(); selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj)); } } @Override protected void processUpdate(Update update, int index, String sql, Object obj) { Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false); if (null != sqlSegment) { update.setWhere(sqlSegment); } } @Override protected void processDelete(Delete delete, int index, String sql, Object obj) { Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false); if (null != sqlSegment) { delete.setWhere(sqlSegment); } } /** * 设置 where 条件 * * @param plainSelect 查询对象 * @param mappedStatementId 执行方法id */ protected void setWhere(PlainSelect plainSelect, String mappedStatementId) { Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true); if (null != sqlSegment) { plainSelect.setWhere(sqlSegment); } } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/interceptor/PlusWebInvokeTimeInterceptor.java ================================================ package top.flya.framework.interceptor; import cn.hutool.core.io.IoUtil; import cn.hutool.core.map.MapUtil; import com.alibaba.ttl.TransmittableThreadLocal; import top.flya.common.filter.RepeatedlyRequestWrapper; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.StopWatch; import org.springframework.http.MediaType; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.util.Map; /** * web的调用时间统计拦截器 * dev环境有效 * * @author Lion Li * @since 3.3.0 */ @Slf4j public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { private final String prodProfile = "prod"; private final TransmittableThreadLocal invokeTimeTL = new TransmittableThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!prodProfile.equals(SpringUtils.getActiveProfile())) { String url = request.getMethod() + " " + request.getRequestURI(); // 打印请求参数 if (isJsonRequest(request)) { String jsonParam = ""; if (request instanceof RepeatedlyRequestWrapper) { BufferedReader reader = request.getReader(); jsonParam = IoUtil.read(reader); } log.debug("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); } else { Map parameterMap = request.getParameterMap(); if (MapUtil.isNotEmpty(parameterMap)) { String parameters = JsonUtils.toJsonString(parameterMap); log.debug("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); } else { log.debug("[PLUS]开始请求 => URL[{}],无参数", url); } } StopWatch stopWatch = new StopWatch(); invokeTimeTL.set(stopWatch); stopWatch.start(); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { if (!prodProfile.equals(SpringUtils.getActiveProfile())) { StopWatch stopWatch = invokeTimeTL.get(); stopWatch.stop(); log.debug("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime()); invokeTimeTL.remove(); } } /** * 判断本次请求的数据类型是否为json * * @param request request * @return boolean */ private boolean isJsonRequest(HttpServletRequest request) { String contentType = request.getContentType(); if (contentType != null) { return StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE); } return false; } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/jackson/BigNumberSerializer.java ================================================ package top.flya.framework.jackson; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.ser.std.NumberSerializer; import java.io.IOException; /** * 超出 JS 最大最小值 处理 * * @author Lion Li */ @JacksonStdImpl public class BigNumberSerializer extends NumberSerializer { /** * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来 */ private static final long MAX_SAFE_INTEGER = 9007199254740991L; private static final long MIN_SAFE_INTEGER = -9007199254740991L; /** * 提供实例 */ public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class); public BigNumberSerializer(Class rawType) { super(rawType); } @Override public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { // 超出范围 序列化位字符串 if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { super.serialize(value, gen, provider); } else { gen.writeString(value.toString()); } } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/listener/UserActionListener.java ================================================ package top.flya.framework.listener; import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.stp.SaLoginModel; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import top.flya.common.constant.CacheConstants; import top.flya.common.core.domain.dto.UserOnlineDTO; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.enums.UserType; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.ip.AddressUtils; import top.flya.common.utils.redis.RedisUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.time.Duration; /** * 用户行为 侦听器的实现 * * @author Lion Li */ @RequiredArgsConstructor @Component @Slf4j public class UserActionListener implements SaTokenListener { private final SaTokenConfig tokenConfig; /** * 每次登录时触发 */ @Override public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { UserType userType = UserType.getUserType(loginId.toString()); if (userType == UserType.SYS_USER) { UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); String ip = ServletUtils.getClientIP(); LoginUser user = LoginHelper.getLoginUser(); UserOnlineDTO dto = new UserOnlineDTO(); dto.setIpaddr(ip); dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); dto.setBrowser(userAgent.getBrowser().getName()); dto.setOs(userAgent.getOs().getName()); dto.setLoginTime(System.currentTimeMillis()); dto.setTokenId(tokenValue); dto.setUserName(user.getUsername()); dto.setDeptName(user.getDeptName()); if(tokenConfig.getTimeout() == -1) { RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); } else { RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); } log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); } else if (userType == UserType.APP_USER) { // app端 自行根据业务编写 } } /** * 每次注销时触发 */ @Override public void doLogout(String loginType, Object loginId, String tokenValue) { RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); } /** * 每次被踢下线时触发 */ @Override public void doKickout(String loginType, Object loginId, String tokenValue) { RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); log.info("user doLogoutByLoginId, userId:{}, token:{}", loginId, tokenValue); } /** * 每次被顶下线时触发 */ @Override public void doReplaced(String loginType, Object loginId, String tokenValue) { RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); } /** * 每次被封禁时触发 */ @Override public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { } /** * 每次被解封时触发 */ @Override public void doUntieDisable(String loginType, Object loginId, String service) { } /** * 每次打开二级认证时触发 */ @Override public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { } /** * 每次创建Session时触发 */ @Override public void doCloseSafe(String loginType, String tokenValue, String service) { } /** * 每次创建Session时触发 */ @Override public void doCreateSession(String id) { } /** * 每次注销Session时触发 */ @Override public void doLogoutSession(String id) { } /** * 每次Token续期时触发 */ @Override public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/manager/EncryptorManager.java ================================================ package top.flya.framework.manager; import cn.hutool.core.util.ReflectUtil; import top.flya.common.annotation.EncryptField; import top.flya.common.encrypt.EncryptContext; import top.flya.common.encrypt.IEncryptor; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * 加密管理类 * * @author 老马 * @version 4.6.0 */ @Slf4j public class EncryptorManager { /** * 缓存加密器 */ Map encryptorMap = new ConcurrentHashMap<>(); /** * 类加密字段缓存 */ Map, Set> fieldCache = new ConcurrentHashMap<>(); /** * 获取类加密字段缓存 */ public Set getFieldCache(Class sourceClazz) { return fieldCache.computeIfAbsent(sourceClazz, clazz -> { Field[] declaredFields = clazz.getDeclaredFields(); Set fieldSet = Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) .collect(Collectors.toSet()); for (Field field : fieldSet) { field.setAccessible(true); } return fieldSet; }); } /** * 注册加密执行者到缓存 * * @param encryptContext 加密执行者需要的相关配置参数 */ public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) { if (encryptorMap.containsKey(encryptContext)) { return encryptorMap.get(encryptContext); } IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext); encryptorMap.put(encryptContext, encryptor); return encryptor; } /** * 移除缓存中的加密执行者 * * @param encryptContext 加密执行者需要的相关配置参数 */ public void removeEncryptor(EncryptContext encryptContext) { this.encryptorMap.remove(encryptContext); } /** * 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。 * * @param value 待加密的值 * @param encryptContext 加密相关的配置信息 */ public String encrypt(String value, EncryptContext encryptContext) { IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); return encryptor.encrypt(value, encryptContext.getEncode()); } /** * 根据配置进行解密 * * @param value 待解密的值 * @param encryptContext 加密相关的配置信息 */ public String decrypt(String value, EncryptContext encryptContext) { IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); return encryptor.decrypt(value); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/manager/PlusSpringCacheManager.java ================================================ /** * Copyright (c) 2013-2021 Nikita Koksharov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package top.flya.framework.manager; import top.flya.common.utils.redis.RedisUtils; import org.redisson.api.RMap; import org.redisson.api.RMapCache; import org.redisson.spring.cache.CacheConfig; import org.redisson.spring.cache.RedissonCache; import org.springframework.boot.convert.DurationStyle; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.transaction.TransactionAwareCacheDecorator; import org.springframework.util.StringUtils; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * A {@link org.springframework.cache.CacheManager} implementation * backed by Redisson instance. *

        * 修改 RedissonSpringCacheManager 源码 * 重写 cacheName 处理方法 支持多参数 * * @author Nikita Koksharov * */ @SuppressWarnings("unchecked") public class PlusSpringCacheManager implements CacheManager { private boolean dynamic = true; private boolean allowNullValues = true; private boolean transactionAware = true; Map configMap = new ConcurrentHashMap<>(); ConcurrentMap instanceMap = new ConcurrentHashMap<>(); /** * Creates CacheManager supplied by Redisson instance */ public PlusSpringCacheManager() { } /** * Defines possibility of storing {@code null} values. *

        * Default is true * * @param allowNullValues stores if true */ public void setAllowNullValues(boolean allowNullValues) { this.allowNullValues = allowNullValues; } /** * Defines if cache aware of Spring-managed transactions. * If {@code true} put/evict operations are executed only for successful transaction in after-commit phase. *

        * Default is false * * @param transactionAware cache is transaction aware if true */ public void setTransactionAware(boolean transactionAware) { this.transactionAware = transactionAware; } /** * Defines 'fixed' cache names. * A new cache instance will not be created in dynamic for non-defined names. *

        * `null` parameter setups dynamic mode * * @param names of caches */ public void setCacheNames(Collection names) { if (names != null) { for (String name : names) { getCache(name); } dynamic = false; } else { dynamic = true; } } /** * Set cache config mapped by cache name * * @param config object */ public void setConfig(Map config) { this.configMap = (Map) config; } protected CacheConfig createDefaultConfig() { return new CacheConfig(); } @Override public Cache getCache(String name) { Cache cache = instanceMap.get(name); if (cache != null) { return cache; } if (!dynamic) { return cache; } CacheConfig config = configMap.get(name); if (config == null) { config = createDefaultConfig(); configMap.put(name, config); } // 重写 cacheName 支持多参数 String[] array = StringUtils.delimitedListToStringArray(name, "#"); name = array[0]; if (array.length > 1) { config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis()); } if (array.length > 2) { config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis()); } if (array.length > 3) { config.setMaxSize(Integer.parseInt(array[3])); } if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { return createMap(name, config); } return createMapCache(name, config); } private Cache createMap(String name, CacheConfig config) { RMap map = RedisUtils.getClient().getMap(name); Cache cache = new RedissonCache(map, allowNullValues); if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } Cache oldCache = instanceMap.putIfAbsent(name, cache); if (oldCache != null) { cache = oldCache; } return cache; } private Cache createMapCache(String name, CacheConfig config) { RMapCache map = RedisUtils.getClient().getMapCache(name); Cache cache = new RedissonCache(map, config, allowNullValues); if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } Cache oldCache = instanceMap.putIfAbsent(name, cache); if (oldCache != null) { cache = oldCache; } else { map.setMaxSize(config.getMaxSize()); } return cache; } @Override public Collection getCacheNames() { return Collections.unmodifiableSet(configMap.keySet()); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/manager/ShutdownManager.java ================================================ package top.flya.framework.manager; import top.flya.common.utils.Threads; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.annotation.PreDestroy; import java.util.concurrent.ScheduledExecutorService; /** * 确保应用退出时能关闭后台线程 * * @author Lion Li */ @Slf4j @Component public class ShutdownManager { @Autowired @Qualifier("scheduledExecutorService") private ScheduledExecutorService scheduledExecutorService; @PreDestroy public void destroy() { shutdownAsyncManager(); } /** * 停止异步执行任务 */ private void shutdownAsyncManager() { try { log.info("====关闭后台任务任务线程池===="); Threads.shutdownAndAwaitTermination(scheduledExecutorService); } catch (Exception e) { log.error(e.getMessage(), e); } } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/satoken/dao/PlusSaTokenDao.java ================================================ package top.flya.framework.satoken.dao; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.util.SaFoxUtil; import top.flya.common.utils.redis.RedisUtils; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一) * * @author Lion Li */ public class PlusSaTokenDao implements SaTokenDao { /** * 获取Value,如无返空 */ @Override public String get(String key) { return RedisUtils.getCacheObject(key); } /** * 写入Value,并设定存活时间 (单位: 秒) */ @Override public void set(String key, String value, long timeout) { if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { return; } // 判断是否为永不过期 if (timeout == SaTokenDao.NEVER_EXPIRE) { RedisUtils.setCacheObject(key, value); } else { RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); } } /** * 修修改指定key-value键值对 (过期时间不变) */ @Override public void update(String key, String value) { long expire = getTimeout(key); // -2 = 无此键 if (expire == SaTokenDao.NOT_VALUE_EXPIRE) { return; } this.set(key, value, expire); } /** * 删除Value */ @Override public void delete(String key) { RedisUtils.deleteObject(key); } /** * 获取Value的剩余存活时间 (单位: 秒) */ @Override public long getTimeout(String key) { long timeout = RedisUtils.getTimeToLive(key); return timeout < 0 ? timeout : timeout / 1000; } /** * 修改Value的剩余存活时间 (单位: 秒) */ @Override public void updateTimeout(String key, long timeout) { // 判断是否想要设置为永久 if (timeout == SaTokenDao.NEVER_EXPIRE) { long expire = getTimeout(key); if (expire == SaTokenDao.NEVER_EXPIRE) { // 如果其已经被设置为永久,则不作任何处理 } else { // 如果尚未被设置为永久,那么再次set一次 this.set(key, this.get(key), timeout); } return; } RedisUtils.expire(key, Duration.ofSeconds(timeout)); } /** * 获取Object,如无返空 */ @Override public Object getObject(String key) { return RedisUtils.getCacheObject(key); } /** * 写入Object,并设定存活时间 (单位: 秒) */ @Override public void setObject(String key, Object object, long timeout) { if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { return; } // 判断是否为永不过期 if (timeout == SaTokenDao.NEVER_EXPIRE) { RedisUtils.setCacheObject(key, object); } else { RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout)); } } /** * 更新Object (过期时间不变) */ @Override public void updateObject(String key, Object object) { long expire = getObjectTimeout(key); // -2 = 无此键 if (expire == SaTokenDao.NOT_VALUE_EXPIRE) { return; } this.setObject(key, object, expire); } /** * 删除Object */ @Override public void deleteObject(String key) { RedisUtils.deleteObject(key); } /** * 获取Object的剩余存活时间 (单位: 秒) */ @Override public long getObjectTimeout(String key) { long timeout = RedisUtils.getTimeToLive(key); return timeout < 0 ? timeout : timeout / 1000; } /** * 修改Object的剩余存活时间 (单位: 秒) */ @Override public void updateObjectTimeout(String key, long timeout) { // 判断是否想要设置为永久 if (timeout == SaTokenDao.NEVER_EXPIRE) { long expire = getObjectTimeout(key); if (expire == SaTokenDao.NEVER_EXPIRE) { // 如果其已经被设置为永久,则不作任何处理 } else { // 如果尚未被设置为永久,那么再次set一次 this.setObject(key, this.getObject(key), timeout); } return; } RedisUtils.expire(key, Duration.ofSeconds(timeout)); } /** * 搜索数据 */ @Override public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { Collection keys = RedisUtils.keys(prefix + "*" + keyword + "*"); List list = new ArrayList<>(keys); return SaFoxUtil.searchList(list, start, size, sortType); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/satoken/service/SaPermissionImpl.java ================================================ package top.flya.framework.satoken.service; import cn.dev33.satoken.stp.StpInterface; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.enums.UserType; import top.flya.common.helper.LoginHelper; import java.util.ArrayList; import java.util.List; /** * sa-token 权限管理实现类 * * @author Lion Li */ public class SaPermissionImpl implements StpInterface { /** * 获取菜单权限列表 */ @Override public List getPermissionList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); UserType userType = UserType.getUserType(loginUser.getUserType()); if (userType == UserType.SYS_USER) { return new ArrayList<>(loginUser.getMenuPermission()); } else if (userType == UserType.APP_USER) { // 其他端 自行根据业务编写 } return new ArrayList<>(); } /** * 获取角色权限列表 */ @Override public List getRoleList(Object loginId, String loginType) { LoginUser loginUser = LoginHelper.getLoginUser(); UserType userType = UserType.getUserType(loginUser.getUserType()); if (userType == UserType.SYS_USER) { return new ArrayList<>(loginUser.getRolePermission()); } else if (userType == UserType.APP_USER) { // 其他端 自行根据业务编写 } return new ArrayList<>(); } } ================================================ FILE: ruoyi-framework/src/main/java/top/flya/framework/web/exception/GlobalExceptionHandler.java ================================================ package top.flya.framework.web.exception; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotRoleException; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpStatus; import top.flya.common.core.domain.R; import top.flya.common.exception.DemoModeException; import top.flya.common.exception.ServiceException; import top.flya.common.utils.StreamUtils; import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.MyBatisSystemException; import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.dao.DuplicateKeyException; import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; /** * 全局异常处理器 * * @author Lion Li */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * 权限码异常 */ @ExceptionHandler(NotPermissionException.class) public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); } /** * 角色权限异常 */ @ExceptionHandler(NotRoleException.class) public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); } /** * 认证失败 */ @ExceptionHandler(NotLoginException.class) public R handleNotLoginException(NotLoginException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage()); return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); } /** * 请求方式不支持 */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); return R.fail(e.getMessage()); } /** * 主键或UNIQUE索引,数据重复异常 */ @ExceptionHandler(DuplicateKeyException.class) public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); return R.fail("数据库中已存在该记录,请联系管理员确认"); } /** * Mybatis系统异常 通用处理 */ @ExceptionHandler(MyBatisSystemException.class) public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); String message = e.getMessage(); if (message.contains("CannotFindDataSourceException")) { log.error("请求地址'{}', 未找到数据源", requestURI); return R.fail("未找到数据源,请联系管理员确认"); } log.error("请求地址'{}', Mybatis系统异常", requestURI, e); return R.fail(message); } /** * 业务异常 */ @ExceptionHandler(ServiceException.class) public R handleServiceException(ServiceException e, HttpServletRequest request) { log.error(e.getMessage(), e); Integer code = e.getCode(); return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage()); } /** * 拦截未知的运行时异常 */ @ExceptionHandler(RuntimeException.class) public R handleRuntimeException(RuntimeException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',发生未知异常.", requestURI, e); return R.fail(e.getMessage()); } /** * 系统异常 */ @ExceptionHandler(Exception.class) public R handleException(Exception e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}',发生系统异常.", requestURI, e); return R.fail(e.getMessage()); } /** * 自定义验证异常 */ @ExceptionHandler(BindException.class) public R handleBindException(BindException e) { log.error(e.getMessage(), e); String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", "); return R.fail(message); } /** * 自定义验证异常 */ @ExceptionHandler(ConstraintViolationException.class) public R constraintViolationException(ConstraintViolationException e) { log.error(e.getMessage(), e); String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", "); return R.fail(message); } /** * 自定义验证异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error(e.getMessage(), e); String message = e.getBindingResult().getFieldError().getDefaultMessage(); return R.fail(message); } /** * 演示模式异常 */ @ExceptionHandler(DemoModeException.class) public R handleDemoModeException(DemoModeException e) { return R.fail("演示模式,不允许操作"); } } ================================================ FILE: ruoyi-generator/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 ruoyi-generator generator代码生成 org.apache.velocity velocity-engine-core com.ruoyi ruoyi-common ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/config/GenConfig.java ================================================ package top.flya.generator.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; /** * 读取代码生成相关配置 * * @author ruoyi */ @Component @ConfigurationProperties(prefix = "gen") @PropertySource(value = {"classpath:generator.yml"}, encoding = "UTF-8") public class GenConfig { /** * 作者 */ public static String author; /** * 生成包路径 */ public static String packageName; /** * 自动去除表前缀,默认是false */ public static boolean autoRemovePre; /** * 表前缀(类名不会包含表前缀) */ public static String tablePrefix; public static String getAuthor() { return author; } @Value("${author}") public void setAuthor(String author) { GenConfig.author = author; } public static String getPackageName() { return packageName; } @Value("${packageName}") public void setPackageName(String packageName) { GenConfig.packageName = packageName; } public static boolean getAutoRemovePre() { return autoRemovePre; } @Value("${autoRemovePre}") public void setAutoRemovePre(boolean autoRemovePre) { GenConfig.autoRemovePre = autoRemovePre; } public static String getTablePrefix() { return tablePrefix; } @Value("${tablePrefix}") public void setTablePrefix(String tablePrefix) { GenConfig.tablePrefix = tablePrefix; } } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/controller/GenController.java ================================================ package top.flya.generator.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.convert.Convert; import cn.hutool.core.io.IoUtil; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.page.TableDataInfo; import top.flya.common.enums.BusinessType; import top.flya.generator.domain.GenTable; import top.flya.generator.domain.GenTableColumn; import top.flya.generator.service.IGenTableService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 代码生成 操作处理 * * @author Lion Li */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/tool/gen") public class GenController extends BaseController { private final IGenTableService genTableService; /** * 查询代码生成列表 */ @SaCheckPermission("tool:gen:list") @GetMapping("/list") public TableDataInfo genList(GenTable genTable, PageQuery pageQuery) { return genTableService.selectPageGenTableList(genTable, pageQuery); } /** * 修改代码生成业务 * * @param tableId 表ID */ @SaCheckPermission("tool:gen:query") @GetMapping(value = "/{tableId}") public R> getInfo(@PathVariable Long tableId) { GenTable table = genTableService.selectGenTableById(tableId); List tables = genTableService.selectGenTableAll(); List list = genTableService.selectGenTableColumnListByTableId(tableId); Map map = new HashMap(); map.put("info", table); map.put("rows", list); map.put("tables", tables); return R.ok(map); } /** * 查询数据库列表 */ @SaCheckPermission("tool:gen:list") @GetMapping("/db/list") public TableDataInfo dataList(GenTable genTable, PageQuery pageQuery) { return genTableService.selectPageDbTableList(genTable, pageQuery); } /** * 查询数据表字段列表 * * @param tableId 表ID */ @SaCheckPermission("tool:gen:list") @GetMapping(value = "/column/{tableId}") public TableDataInfo columnList(Long tableId) { TableDataInfo dataInfo = new TableDataInfo<>(); List list = genTableService.selectGenTableColumnListByTableId(tableId); dataInfo.setRows(list); dataInfo.setTotal(list.size()); return dataInfo; } /** * 导入表结构(保存) * * @param tables 表名串 */ @SaCheckPermission("tool:gen:import") @Log(title = "代码生成", businessType = BusinessType.IMPORT) @PostMapping("/importTable") public R importTableSave(String tables) { String[] tableNames = Convert.toStrArray(tables); // 查询表信息 List tableList = genTableService.selectDbTableListByNames(tableNames); genTableService.importGenTable(tableList); return R.ok(); } /** * 修改保存代码生成业务 */ @SaCheckPermission("tool:gen:edit") @Log(title = "代码生成", businessType = BusinessType.UPDATE) @PutMapping public R editSave(@Validated @RequestBody GenTable genTable) { genTableService.validateEdit(genTable); genTableService.updateGenTable(genTable); return R.ok(); } /** * 删除代码生成 * * @param tableIds 表ID串 */ @SaCheckPermission("tool:gen:remove") @Log(title = "代码生成", businessType = BusinessType.DELETE) @DeleteMapping("/{tableIds}") public R remove(@PathVariable Long[] tableIds) { genTableService.deleteGenTableByIds(tableIds); return R.ok(); } /** * 预览代码 * * @param tableId 表ID */ @SaCheckPermission("tool:gen:preview") @GetMapping("/preview/{tableId}") public R> preview(@PathVariable("tableId") Long tableId) throws IOException { Map dataMap = genTableService.previewCode(tableId); return R.ok(dataMap); } /** * 生成代码(下载方式) * * @param tableName 表名 */ @SaCheckPermission("tool:gen:code") @Log(title = "代码生成", businessType = BusinessType.GENCODE) @GetMapping("/download/{tableName}") public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException { byte[] data = genTableService.downloadCode(tableName); genCode(response, data); } /** * 生成代码(自定义路径) * * @param tableName 表名 */ @SaCheckPermission("tool:gen:code") @Log(title = "代码生成", businessType = BusinessType.GENCODE) @GetMapping("/genCode/{tableName}") public R genCode(@PathVariable("tableName") String tableName) { genTableService.generatorCode(tableName); return R.ok(); } /** * 同步数据库 * * @param tableName 表名 */ @SaCheckPermission("tool:gen:edit") @Log(title = "代码生成", businessType = BusinessType.UPDATE) @GetMapping("/synchDb/{tableName}") public R synchDb(@PathVariable("tableName") String tableName) { genTableService.synchDb(tableName); return R.ok(); } /** * 批量生成代码 * * @param tables 表名串 */ @SaCheckPermission("tool:gen:code") @Log(title = "代码生成", businessType = BusinessType.GENCODE) @GetMapping("/batchGenCode") public void batchGenCode(HttpServletResponse response, String tables) throws IOException { String[] tableNames = Convert.toStrArray(tables); byte[] data = genTableService.downloadCode(tableNames); genCode(response, data); } /** * 生成zip文件 */ private void genCode(HttpServletResponse response, byte[] data) throws IOException { response.reset(); response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); response.addHeader("Content-Length", "" + data.length); response.setContentType("application/octet-stream; charset=UTF-8"); IoUtil.write(response.getOutputStream(), false, data); } } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/domain/GenTable.java ================================================ package top.flya.generator.domain; import com.baomidou.mybatisplus.annotation.*; import top.flya.common.constant.GenConstants; import top.flya.common.core.domain.BaseEntity; import top.flya.common.utils.StringUtils; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.lang3.ArrayUtils; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import java.util.List; /** * 业务表 gen_table * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("gen_table") public class GenTable extends BaseEntity { /** * 编号 */ @TableId(value = "table_id") private Long tableId; /** * 表名称 */ @NotBlank(message = "表名称不能为空") private String tableName; /** * 表描述 */ @NotBlank(message = "表描述不能为空") private String tableComment; /** * 关联父表的表名 */ private String subTableName; /** * 本表关联父表的外键名 */ private String subTableFkName; /** * 实体类名称(首字母大写) */ @NotBlank(message = "实体类名称不能为空") private String className; /** * 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ private String tplCategory; /** * 生成包路径 */ @NotBlank(message = "生成包路径不能为空") private String packageName; /** * 生成模块名 */ @NotBlank(message = "生成模块名不能为空") private String moduleName; /** * 生成业务名 */ @NotBlank(message = "生成业务名不能为空") private String businessName; /** * 生成功能名 */ @NotBlank(message = "生成功能名不能为空") private String functionName; /** * 生成作者 */ @NotBlank(message = "作者不能为空") private String functionAuthor; /** * 生成代码方式(0zip压缩包 1自定义路径) */ private String genType; /** * 生成路径(不填默认项目路径) */ @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) private String genPath; /** * 主键信息 */ @TableField(exist = false) private GenTableColumn pkColumn; /** * 子表信息 */ @TableField(exist = false) private GenTable subTable; /** * 表列信息 */ @Valid @TableField(exist = false) private List columns; /** * 其它生成选项 */ private String options; /** * 备注 */ private String remark; /** * 树编码字段 */ @TableField(exist = false) private String treeCode; /** * 树父编码字段 */ @TableField(exist = false) private String treeParentCode; /** * 树名称字段 */ @TableField(exist = false) private String treeName; /* * 菜单id列表 */ @TableField(exist = false) private List menuIds; /** * 上级菜单ID字段 */ @TableField(exist = false) private String parentMenuId; /** * 上级菜单名称字段 */ @TableField(exist = false) private String parentMenuName; public boolean isSub() { return isSub(this.tplCategory); } public static boolean isSub(String tplCategory) { return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); } public boolean isTree() { return isTree(this.tplCategory); } public static boolean isTree(String tplCategory) { return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); } public boolean isCrud() { return isCrud(this.tplCategory); } public static boolean isCrud(String tplCategory) { return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); } public boolean isSuperColumn(String javaField) { return isSuperColumn(this.tplCategory, javaField); } public static boolean isSuperColumn(String tplCategory, String javaField) { if (isTree(tplCategory)) { return StringUtils.equalsAnyIgnoreCase(javaField, ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); } return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); } } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/domain/GenTableColumn.java ================================================ package top.flya.generator.domain; import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.core.domain.BaseEntity; import top.flya.common.utils.StringUtils; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.ibatis.type.JdbcType; import javax.validation.constraints.NotBlank; /** * 代码生成业务字段表 gen_table_column * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("gen_table_column") public class GenTableColumn extends BaseEntity { /** * 编号 */ @TableId(value = "column_id") private Long columnId; /** * 归属表编号 */ private Long tableId; /** * 列名称 */ private String columnName; /** * 列描述 */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String columnComment; /** * 列类型 */ private String columnType; /** * JAVA类型 */ private String javaType; /** * JAVA字段名 */ @NotBlank(message = "Java属性不能为空") private String javaField; /** * 是否主键(1是) */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String isPk; /** * 是否自增(1是) */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String isIncrement; /** * 是否必填(1是) */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String isRequired; /** * 是否为插入字段(1是) */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String isInsert; /** * 是否编辑字段(1是) */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String isEdit; /** * 是否列表字段(1是) */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String isList; /** * 是否查询字段(1是) */ @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR) private String isQuery; /** * 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ private String queryType; /** * 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ private String htmlType; /** * 字典类型 */ private String dictType; /** * 排序 */ private Integer sort; public String getCapJavaField() { return StringUtils.capitalize(javaField); } public boolean isPk() { return isPk(this.isPk); } public boolean isPk(String isPk) { return isPk != null && StringUtils.equals("1", isPk); } public boolean isIncrement() { return isIncrement(this.isIncrement); } public boolean isIncrement(String isIncrement) { return isIncrement != null && StringUtils.equals("1", isIncrement); } public boolean isRequired() { return isRequired(this.isRequired); } public boolean isRequired(String isRequired) { return isRequired != null && StringUtils.equals("1", isRequired); } public boolean isInsert() { return isInsert(this.isInsert); } public boolean isInsert(String isInsert) { return isInsert != null && StringUtils.equals("1", isInsert); } public boolean isEdit() { return isInsert(this.isEdit); } public boolean isEdit(String isEdit) { return isEdit != null && StringUtils.equals("1", isEdit); } public boolean isList() { return isList(this.isList); } public boolean isList(String isList) { return isList != null && StringUtils.equals("1", isList); } public boolean isQuery() { return isQuery(this.isQuery); } public boolean isQuery(String isQuery) { return isQuery != null && StringUtils.equals("1", isQuery); } public boolean isSuperColumn() { return isSuperColumn(this.javaField); } public static boolean isSuperColumn(String javaField) { return StringUtils.equalsAnyIgnoreCase(javaField, // BaseEntity "createBy", "createTime", "updateBy", "updateTime", // TreeEntity "parentName", "parentId"); } public boolean isUsableColumn() { return isUsableColumn(javaField); } public static boolean isUsableColumn(String javaField) { // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); } public String readConverterExp() { String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); StringBuffer sb = new StringBuffer(); if (StringUtils.isNotEmpty(remarks)) { for (String value : remarks.split(" ")) { if (StringUtils.isNotEmpty(value)) { Object startStr = value.subSequence(0, 1); String endStr = value.substring(1); sb.append(StringUtils.EMPTY).append(startStr).append("=").append(endStr).append(StringUtils.SEPARATOR); } } return sb.deleteCharAt(sb.length() - 1).toString(); } else { return this.columnComment; } } } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/mapper/GenTableColumnMapper.java ================================================ package top.flya.generator.mapper; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.generator.domain.GenTableColumn; import java.util.List; /** * 业务字段 数据层 * * @author Lion Li */ @InterceptorIgnore(dataPermission = "true") public interface GenTableColumnMapper extends BaseMapperPlus { /** * 根据表名称查询列信息 * * @param tableName 表名称 * @return 列信息 */ List selectDbTableColumnsByName(String tableName); } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/mapper/GenTableMapper.java ================================================ package top.flya.generator.mapper; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.generator.domain.GenTable; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 业务 数据层 * * @author Lion Li */ @InterceptorIgnore(dataPermission = "true") public interface GenTableMapper extends BaseMapperPlus { /** * 查询据库列表 * * @param genTable 查询条件 * @return 数据库表集合 */ Page selectPageDbTableList(@Param("page") Page page, @Param("genTable") GenTable genTable); /** * 查询据库列表 * * @param tableNames 表名称组 * @return 数据库表集合 */ List selectDbTableListByNames(String[] tableNames); /** * 查询所有表信息 * * @return 表信息集合 */ List selectGenTableAll(); /** * 查询表ID业务信息 * * @param id 业务ID * @return 业务信息 */ GenTable selectGenTableById(Long id); /** * 查询表名称业务信息 * * @param tableName 表名称 * @return 业务信息 */ GenTable selectGenTableByName(String tableName); } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/service/GenTableServiceImpl.java ================================================ package top.flya.generator.service; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.constant.Constants; import top.flya.common.constant.GenConstants; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.exception.ServiceException; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.file.FileUtils; import top.flya.generator.domain.GenTable; import top.flya.generator.domain.GenTableColumn; import top.flya.generator.mapper.GenTableColumnMapper; import top.flya.generator.mapper.GenTableMapper; import top.flya.generator.util.GenUtils; import top.flya.generator.util.VelocityInitializer; import top.flya.generator.util.VelocityUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * 业务 服务层实现 * * @author Lion Li */ @DS("#header.datasource") @Slf4j @RequiredArgsConstructor @Service public class GenTableServiceImpl implements IGenTableService { private final GenTableMapper baseMapper; private final GenTableColumnMapper genTableColumnMapper; private final IdentifierGenerator identifierGenerator; /** * 查询业务字段列表 * * @param tableId 业务字段编号 * @return 业务字段集合 */ @Override public List selectGenTableColumnListByTableId(Long tableId) { return genTableColumnMapper.selectList(new LambdaQueryWrapper() .eq(GenTableColumn::getTableId, tableId) .orderByAsc(GenTableColumn::getSort)); } /** * 查询业务信息 * * @param id 业务ID * @return 业务信息 */ @Override public GenTable selectGenTableById(Long id) { GenTable genTable = baseMapper.selectGenTableById(id); setTableFromOptions(genTable); return genTable; } @Override public TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery) { Page page = baseMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable)); return TableDataInfo.build(page); } private QueryWrapper buildGenTableQueryWrapper(GenTable genTable) { Map params = genTable.getParams(); QueryWrapper wrapper = Wrappers.query(); wrapper.like(StringUtils.isNotBlank(genTable.getTableName()), "lower(table_name)", StringUtils.lowerCase(genTable.getTableName())) .like(StringUtils.isNotBlank(genTable.getTableComment()), "lower(table_comment)", StringUtils.lowerCase(genTable.getTableComment())) .between(params.get("beginTime") != null && params.get("endTime") != null, "create_time", params.get("beginTime"), params.get("endTime")); return wrapper; } @Override public TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery) { Page page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable); return TableDataInfo.build(page); } /** * 查询据库列表 * * @param tableNames 表名称组 * @return 数据库表集合 */ @Override public List selectDbTableListByNames(String[] tableNames) { return baseMapper.selectDbTableListByNames(tableNames); } /** * 查询所有表信息 * * @return 表信息集合 */ @Override public List selectGenTableAll() { return baseMapper.selectGenTableAll(); } /** * 修改业务 * * @param genTable 业务信息 * @return 结果 */ @Transactional(rollbackFor = Exception.class) @Override public void updateGenTable(GenTable genTable) { String options = JsonUtils.toJsonString(genTable.getParams()); genTable.setOptions(options); int row = baseMapper.updateById(genTable); if (row > 0) { for (GenTableColumn cenTableColumn : genTable.getColumns()) { genTableColumnMapper.updateById(cenTableColumn); } } } /** * 删除业务对象 * * @param tableIds 需要删除的数据ID * @return 结果 */ @Transactional(rollbackFor = Exception.class) @Override public void deleteGenTableByIds(Long[] tableIds) { List ids = Arrays.asList(tableIds); baseMapper.deleteBatchIds(ids); genTableColumnMapper.delete(new LambdaQueryWrapper().in(GenTableColumn::getTableId, ids)); } /** * 导入表结构 * * @param tableList 导入表列表 */ @Transactional(rollbackFor = Exception.class) @Override public void importGenTable(List tableList) { String operName = LoginHelper.getUsername(); try { for (GenTable table : tableList) { String tableName = table.getTableName(); GenUtils.initTable(table, operName); int row = baseMapper.insert(table); if (row > 0) { // 保存列信息 List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); List saveColumns = new ArrayList<>(); for (GenTableColumn column : genTableColumns) { GenUtils.initColumnField(column, table); saveColumns.add(column); } if (CollUtil.isNotEmpty(saveColumns)) { genTableColumnMapper.insertBatch(saveColumns); } } } } catch (Exception e) { throw new ServiceException("导入失败:" + e.getMessage()); } } /** * 预览代码 * * @param tableId 表编号 * @return 预览数据列表 */ @Override public Map previewCode(Long tableId) { Map dataMap = new LinkedHashMap<>(); // 查询表信息 GenTable table = baseMapper.selectGenTableById(tableId); List menuIds = new ArrayList<>(); for (int i = 0; i < 6; i++) { menuIds.add(identifierGenerator.nextId(null).longValue()); } table.setMenuIds(menuIds); // 设置主子表信息 setSubTable(table); // 设置主键列信息 setPkColumn(table); VelocityInitializer.initVelocity(); VelocityContext context = VelocityUtils.prepareContext(table); // 获取模板列表 List templates = VelocityUtils.getTemplateList(table.getTplCategory()); for (String template : templates) { // 渲染模板 StringWriter sw = new StringWriter(); Template tpl = Velocity.getTemplate(template, Constants.UTF8); tpl.merge(context, sw); dataMap.put(template, sw.toString()); } return dataMap; } /** * 生成代码(下载方式) * * @param tableName 表名称 * @return 数据 */ @Override public byte[] downloadCode(String tableName) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ZipOutputStream zip = new ZipOutputStream(outputStream); generatorCode(tableName, zip); IoUtil.close(zip); return outputStream.toByteArray(); } /** * 生成代码(自定义路径) * * @param tableName 表名称 */ @Override public void generatorCode(String tableName) { // 查询表信息 GenTable table = baseMapper.selectGenTableByName(tableName); // 设置主子表信息 setSubTable(table); // 设置主键列信息 setPkColumn(table); VelocityInitializer.initVelocity(); VelocityContext context = VelocityUtils.prepareContext(table); // 获取模板列表 List templates = VelocityUtils.getTemplateList(table.getTplCategory()); for (String template : templates) { if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) { // 渲染模板 StringWriter sw = new StringWriter(); Template tpl = Velocity.getTemplate(template, Constants.UTF8); tpl.merge(context, sw); try { String path = getGenPath(table, template); FileUtils.writeUtf8String(sw.toString(), path); } catch (Exception e) { throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); } } } } /** * 同步数据库 * * @param tableName 表名称 */ @Transactional(rollbackFor = Exception.class) @Override public void synchDb(String tableName) { GenTable table = baseMapper.selectGenTableByName(tableName); List tableColumns = table.getColumns(); Map tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName); List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); if (CollUtil.isEmpty(dbTableColumns)) { throw new ServiceException("同步数据失败,原表结构不存在"); } List dbTableColumnNames = StreamUtils.toList(dbTableColumns, GenTableColumn::getColumnName); List saveColumns = new ArrayList<>(); dbTableColumns.forEach(column -> { GenUtils.initColumnField(column, table); if (tableColumnMap.containsKey(column.getColumnName())) { GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); column.setColumnId(prevColumn.getColumnId()); if (column.isList()) { // 如果是列表,继续保留查询方式/字典类型选项 column.setDictType(prevColumn.getDictType()); column.setQueryType(prevColumn.getQueryType()); } if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() && (column.isInsert() || column.isEdit()) && ((column.isUsableColumn()) || (!column.isSuperColumn()))) { // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 column.setIsRequired(prevColumn.getIsRequired()); column.setHtmlType(prevColumn.getHtmlType()); } } saveColumns.add(column); }); if (CollUtil.isNotEmpty(saveColumns)) { genTableColumnMapper.insertOrUpdateBatch(saveColumns); } List delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName())); if (CollUtil.isNotEmpty(delColumns)) { List ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId); genTableColumnMapper.deleteBatchIds(ids); } } /** * 批量生成代码(下载方式) * * @param tableNames 表数组 * @return 数据 */ @Override public byte[] downloadCode(String[] tableNames) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ZipOutputStream zip = new ZipOutputStream(outputStream); for (String tableName : tableNames) { generatorCode(tableName, zip); } IoUtil.close(zip); return outputStream.toByteArray(); } /** * 查询表信息并生成代码 */ private void generatorCode(String tableName, ZipOutputStream zip) { // 查询表信息 GenTable table = baseMapper.selectGenTableByName(tableName); List menuIds = new ArrayList<>(); for (int i = 0; i < 6; i++) { menuIds.add(identifierGenerator.nextId(null).longValue()); } table.setMenuIds(menuIds); // 设置主子表信息 setSubTable(table); // 设置主键列信息 setPkColumn(table); VelocityInitializer.initVelocity(); VelocityContext context = VelocityUtils.prepareContext(table); // 获取模板列表 List templates = VelocityUtils.getTemplateList(table.getTplCategory()); for (String template : templates) { // 渲染模板 StringWriter sw = new StringWriter(); Template tpl = Velocity.getTemplate(template, Constants.UTF8); tpl.merge(context, sw); try { // 添加到zip zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString()); IoUtil.close(sw); zip.flush(); zip.closeEntry(); } catch (IOException e) { log.error("渲染模板失败,表名:" + table.getTableName(), e); } } } /** * 修改保存参数校验 * * @param genTable 业务信息 */ @Override public void validateEdit(GenTable genTable) { if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) { String options = JsonUtils.toJsonString(genTable.getParams()); Dict paramsObj = JsonUtils.parseMap(options); if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_CODE))) { throw new ServiceException("树编码字段不能为空"); } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_PARENT_CODE))) { throw new ServiceException("树父编码字段不能为空"); } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_NAME))) { throw new ServiceException("树名称字段不能为空"); } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) { if (StringUtils.isEmpty(genTable.getSubTableName())) { throw new ServiceException("关联子表的表名不能为空"); } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) { throw new ServiceException("子表关联的外键名不能为空"); } } } } /** * 设置主键列信息 * * @param table 业务表信息 */ public void setPkColumn(GenTable table) { for (GenTableColumn column : table.getColumns()) { if (column.isPk()) { table.setPkColumn(column); break; } } if (ObjectUtil.isNull(table.getPkColumn())) { table.setPkColumn(table.getColumns().get(0)); } if (GenConstants.TPL_SUB.equals(table.getTplCategory())) { for (GenTableColumn column : table.getSubTable().getColumns()) { if (column.isPk()) { table.getSubTable().setPkColumn(column); break; } } if (ObjectUtil.isNull(table.getSubTable().getPkColumn())) { table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); } } } /** * 设置主子表信息 * * @param table 业务表信息 */ public void setSubTable(GenTable table) { String subTableName = table.getSubTableName(); if (StringUtils.isNotEmpty(subTableName)) { table.setSubTable(baseMapper.selectGenTableByName(subTableName)); } } /** * 设置代码生成其他选项值 * * @param genTable 设置后的生成对象 */ public void setTableFromOptions(GenTable genTable) { Dict paramsObj = JsonUtils.parseMap(genTable.getOptions()); if (ObjectUtil.isNotNull(paramsObj)) { String treeCode = paramsObj.getStr(GenConstants.TREE_CODE); String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE); String treeName = paramsObj.getStr(GenConstants.TREE_NAME); String parentMenuId = paramsObj.getStr(GenConstants.PARENT_MENU_ID); String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME); genTable.setTreeCode(treeCode); genTable.setTreeParentCode(treeParentCode); genTable.setTreeName(treeName); genTable.setParentMenuId(parentMenuId); genTable.setParentMenuName(parentMenuName); } } /** * 获取代码生成地址 * * @param table 业务表信息 * @param template 模板文件路径 * @return 生成地址 */ public static String getGenPath(GenTable table, String template) { String genPath = table.getGenPath(); if (StringUtils.equals(genPath, "/")) { return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); } return genPath + File.separator + VelocityUtils.getFileName(template, table); } } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/service/IGenTableService.java ================================================ package top.flya.generator.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.generator.domain.GenTable; import top.flya.generator.domain.GenTableColumn; import java.util.List; import java.util.Map; /** * 业务 服务层 * * @author Lion Li */ public interface IGenTableService { /** * 查询业务字段列表 * * @param tableId 业务字段编号 * @return 业务字段集合 */ List selectGenTableColumnListByTableId(Long tableId); /** * 查询业务列表 * * @param genTable 业务信息 * @return 业务集合 */ TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery); /** * 查询据库列表 * * @param genTable 业务信息 * @return 数据库表集合 */ TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery); /** * 查询据库列表 * * @param tableNames 表名称组 * @return 数据库表集合 */ List selectDbTableListByNames(String[] tableNames); /** * 查询所有表信息 * * @return 表信息集合 */ List selectGenTableAll(); /** * 查询业务信息 * * @param id 业务ID * @return 业务信息 */ GenTable selectGenTableById(Long id); /** * 修改业务 * * @param genTable 业务信息 * @return 结果 */ void updateGenTable(GenTable genTable); /** * 删除业务信息 * * @param tableIds 需要删除的表数据ID * @return 结果 */ void deleteGenTableByIds(Long[] tableIds); /** * 导入表结构 * * @param tableList 导入表列表 */ void importGenTable(List tableList); /** * 预览代码 * * @param tableId 表编号 * @return 预览数据列表 */ Map previewCode(Long tableId); /** * 生成代码(下载方式) * * @param tableName 表名称 * @return 数据 */ byte[] downloadCode(String tableName); /** * 生成代码(自定义路径) * * @param tableName 表名称 * @return 数据 */ void generatorCode(String tableName); /** * 同步数据库 * * @param tableName 表名称 */ void synchDb(String tableName); /** * 批量生成代码(下载方式) * * @param tableNames 表数组 * @return 数据 */ byte[] downloadCode(String[] tableNames); /** * 修改保存参数校验 * * @param genTable 业务信息 */ void validateEdit(GenTable genTable); } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/util/GenUtils.java ================================================ package top.flya.generator.util; import top.flya.common.constant.GenConstants; import top.flya.common.utils.StringUtils; import top.flya.generator.config.GenConfig; import top.flya.generator.domain.GenTable; import top.flya.generator.domain.GenTableColumn; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.commons.lang3.RegExUtils; import java.util.Arrays; /** * 代码生成器 工具类 * * @author ruoyi */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class GenUtils { /** * 初始化表信息 */ public static void initTable(GenTable genTable, String operName) { genTable.setClassName(convertClassName(genTable.getTableName())); genTable.setPackageName(GenConfig.getPackageName()); genTable.setModuleName(getModuleName(GenConfig.getPackageName())); genTable.setBusinessName(getBusinessName(genTable.getTableName())); genTable.setFunctionName(replaceText(genTable.getTableComment())); genTable.setFunctionAuthor(GenConfig.getAuthor()); // genTable.setCreateBy(operName); } /** * 初始化列属性字段 */ public static void initColumnField(GenTableColumn column, GenTable table) { String dataType = getDbType(column.getColumnType()); String columnName = column.getColumnName(); column.setTableId(table.getTableId()); // column.setCreateBy(table.getCreateBy()); // 设置java字段名 column.setJavaField(StringUtils.toCamelCase(columnName)); // 设置默认类型 column.setJavaType(GenConstants.TYPE_STRING); column.setQueryType(GenConstants.QUERY_EQ); if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) { // 字符串长度超过500设置为文本域 Integer columnLength = getColumnLength(column.getColumnType()); String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; column.setHtmlType(htmlType); } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) { column.setJavaType(GenConstants.TYPE_DATE); column.setHtmlType(GenConstants.HTML_DATETIME); } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) { column.setHtmlType(GenConstants.HTML_INPUT); // 如果是浮点型 统一用BigDecimal String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), StringUtils.SEPARATOR); if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) { column.setJavaType(GenConstants.TYPE_BIGDECIMAL); } // 如果是整形 else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) { column.setJavaType(GenConstants.TYPE_INTEGER); } // 长整形 else { column.setJavaType(GenConstants.TYPE_LONG); } } // BO对象 默认插入勾选 if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) { column.setIsInsert(GenConstants.REQUIRE); } // BO对象 默认编辑勾选 if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { column.setIsEdit(GenConstants.REQUIRE); } // BO对象 默认是否必填勾选 if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { column.setIsRequired(GenConstants.REQUIRE); } // VO对象 默认返回勾选 if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) { column.setIsList(GenConstants.REQUIRE); } // BO对象 默认查询勾选 if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) { column.setIsQuery(GenConstants.REQUIRE); } // 查询字段类型 if (StringUtils.endsWithIgnoreCase(columnName, "name")) { column.setQueryType(GenConstants.QUERY_LIKE); } // 状态字段设置单选框 if (StringUtils.endsWithIgnoreCase(columnName, "status")) { column.setHtmlType(GenConstants.HTML_RADIO); } // 类型&性别字段设置下拉框 else if (StringUtils.endsWithIgnoreCase(columnName, "type") || StringUtils.endsWithIgnoreCase(columnName, "sex")) { column.setHtmlType(GenConstants.HTML_SELECT); } // 图片字段设置图片上传控件 else if (StringUtils.endsWithIgnoreCase(columnName, "image")) { column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); } // 文件字段设置文件上传控件 else if (StringUtils.endsWithIgnoreCase(columnName, "file")) { column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); } // 内容字段设置富文本控件 else if (StringUtils.endsWithIgnoreCase(columnName, "content")) { column.setHtmlType(GenConstants.HTML_EDITOR); } } /** * 校验数组是否包含指定值 * * @param arr 数组 * @param targetValue 值 * @return 是否包含 */ public static boolean arraysContains(String[] arr, String targetValue) { return Arrays.asList(arr).contains(targetValue); } /** * 获取模块名 * * @param packageName 包名 * @return 模块名 */ public static String getModuleName(String packageName) { int lastIndex = packageName.lastIndexOf("."); int nameLength = packageName.length(); return StringUtils.substring(packageName, lastIndex + 1, nameLength); } /** * 获取业务名 * * @param tableName 表名 * @return 业务名 */ public static String getBusinessName(String tableName) { int firstIndex = tableName.indexOf("_"); int nameLength = tableName.length(); String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength); businessName = StringUtils.toCamelCase(businessName); return businessName; } /** * 表名转换成Java类名 * * @param tableName 表名称 * @return 类名 */ public static String convertClassName(String tableName) { boolean autoRemovePre = GenConfig.getAutoRemovePre(); String tablePrefix = GenConfig.getTablePrefix(); if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) { String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR); tableName = replaceFirst(tableName, searchList); } return StringUtils.convertToCamelCase(tableName); } /** * 批量替换前缀 * * @param replacementm 替换值 * @param searchList 替换列表 * @return */ public static String replaceFirst(String replacementm, String[] searchList) { String text = replacementm; for (String searchString : searchList) { if (replacementm.startsWith(searchString)) { text = replacementm.replaceFirst(searchString, StringUtils.EMPTY); break; } } return text; } /** * 关键字替换 * * @param text 需要被替换的名字 * @return 替换后的名字 */ public static String replaceText(String text) { return RegExUtils.replaceAll(text, "(?:表|若依)", ""); } /** * 获取数据库类型字段 * * @param columnType 列类型 * @return 截取后的列类型 */ public static String getDbType(String columnType) { if (StringUtils.indexOf(columnType, '(') > 0) { return StringUtils.substringBefore(columnType, "("); } else { return columnType; } } /** * 获取字段长度 * * @param columnType 列类型 * @return 截取后的列类型 */ public static Integer getColumnLength(String columnType) { if (StringUtils.indexOf(columnType, '(') > 0) { String length = StringUtils.substringBetween(columnType, "(", ")"); return Integer.valueOf(length); } else { return 0; } } } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/util/VelocityInitializer.java ================================================ package top.flya.generator.util; import top.flya.common.constant.Constants; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.velocity.app.Velocity; import java.util.Properties; /** * VelocityEngine工厂 * * @author ruoyi */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class VelocityInitializer { /** * 初始化vm方法 */ public static void initVelocity() { Properties p = new Properties(); try { // 加载classpath目录下的vm文件 p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); // 定义字符集 p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); // 初始化Velocity引擎,指定配置Properties Velocity.init(p); } catch (Exception e) { throw new RuntimeException(e); } } } ================================================ FILE: ruoyi-generator/src/main/java/top/flya/generator/util/VelocityUtils.java ================================================ package top.flya.generator.util; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import top.flya.common.constant.GenConstants; import top.flya.common.helper.DataBaseHelper; import top.flya.common.utils.DateUtils; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StringUtils; import top.flya.generator.domain.GenTable; import top.flya.generator.domain.GenTableColumn; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.velocity.VelocityContext; import java.util.*; /** * 模板处理工具类 * * @author ruoyi */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class VelocityUtils { /** * 项目空间路径 */ private static final String PROJECT_PATH = "main/java"; /** * mybatis空间路径 */ private static final String MYBATIS_PATH = "main/resources/mapper"; /** * 默认上级菜单,系统工具 */ private static final String DEFAULT_PARENT_MENU_ID = "3"; /** * 设置模板变量信息 * * @return 模板列表 */ public static VelocityContext prepareContext(GenTable genTable) { String moduleName = genTable.getModuleName(); String businessName = genTable.getBusinessName(); String packageName = genTable.getPackageName(); String tplCategory = genTable.getTplCategory(); String functionName = genTable.getFunctionName(); VelocityContext velocityContext = new VelocityContext(); velocityContext.put("tplCategory", genTable.getTplCategory()); velocityContext.put("tableName", genTable.getTableName()); velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); velocityContext.put("ClassName", genTable.getClassName()); velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); velocityContext.put("moduleName", genTable.getModuleName()); velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); velocityContext.put("businessName", genTable.getBusinessName()); velocityContext.put("basePackage", getPackagePrefix(packageName)); velocityContext.put("packageName", packageName); velocityContext.put("author", genTable.getFunctionAuthor()); velocityContext.put("datetime", DateUtils.getDate()); velocityContext.put("pkColumn", genTable.getPkColumn()); velocityContext.put("importList", getImportList(genTable)); velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); velocityContext.put("columns", genTable.getColumns()); velocityContext.put("table", genTable); velocityContext.put("dicts", getDicts(genTable)); setMenuVelocityContext(velocityContext, genTable); if (GenConstants.TPL_TREE.equals(tplCategory)) { setTreeVelocityContext(velocityContext, genTable); } if (GenConstants.TPL_SUB.equals(tplCategory)) { setSubVelocityContext(velocityContext, genTable); } return velocityContext; } public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) { String options = genTable.getOptions(); Dict paramsObj = JsonUtils.parseMap(options); String parentMenuId = getParentMenuId(paramsObj); context.put("parentMenuId", parentMenuId); } public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) { String options = genTable.getOptions(); Dict paramsObj = JsonUtils.parseMap(options); String treeCode = getTreecode(paramsObj); String treeParentCode = getTreeParentCode(paramsObj); String treeName = getTreeName(paramsObj); context.put("treeCode", treeCode); context.put("treeParentCode", treeParentCode); context.put("treeName", treeName); context.put("expandColumn", getExpandColumn(genTable)); if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE)); } if (paramsObj.containsKey(GenConstants.TREE_NAME)) { context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME)); } } public static void setSubVelocityContext(VelocityContext context, GenTable genTable) { GenTable subTable = genTable.getSubTable(); String subTableName = genTable.getSubTableName(); String subTableFkName = genTable.getSubTableFkName(); String subClassName = genTable.getSubTable().getClassName(); String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); context.put("subTable", subTable); context.put("subTableName", subTableName); context.put("subTableFkName", subTableFkName); context.put("subTableFkClassName", subTableFkClassName); context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); context.put("subClassName", subClassName); context.put("subclassName", StringUtils.uncapitalize(subClassName)); context.put("subImportList", getImportList(genTable.getSubTable())); } /** * 获取模板信息 * * @return 模板列表 */ public static List getTemplateList(String tplCategory) { List templates = new ArrayList(); templates.add("vm/java/domain.java.vm"); templates.add("vm/java/vo.java.vm"); templates.add("vm/java/bo.java.vm"); templates.add("vm/java/mapper.java.vm"); templates.add("vm/java/service.java.vm"); templates.add("vm/java/serviceImpl.java.vm"); templates.add("vm/java/controller.java.vm"); templates.add("vm/xml/mapper.xml.vm"); if (DataBaseHelper.isOracle()) { templates.add("vm/sql/oracle/sql.vm"); } else if (DataBaseHelper.isPostgerSql()) { templates.add("vm/sql/postgres/sql.vm"); } else if (DataBaseHelper.isSqlServer()) { templates.add("vm/sql/sqlserver/sql.vm"); } else { templates.add("vm/sql/sql.vm"); } templates.add("vm/js/api.js.vm"); if (GenConstants.TPL_CRUD.equals(tplCategory)) { templates.add("vm/vue/index.vue.vm"); } else if (GenConstants.TPL_TREE.equals(tplCategory)) { templates.add("vm/vue/index-tree.vue.vm"); } else if (GenConstants.TPL_SUB.equals(tplCategory)) { templates.add("vm/vue/index.vue.vm"); templates.add("vm/java/sub-domain.java.vm"); } return templates; } /** * 获取文件名 */ public static String getFileName(String template, GenTable genTable) { // 文件名称 String fileName = ""; // 包路径 String packageName = genTable.getPackageName(); // 模块名 String moduleName = genTable.getModuleName(); // 大写类名 String className = genTable.getClassName(); // 业务名称 String businessName = genTable.getBusinessName(); String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); String mybatisPath = MYBATIS_PATH + "/" + moduleName; String vuePath = "vue"; if (template.contains("domain.java.vm")) { fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); } if (template.contains("vo.java.vm")) { fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className); } if (template.contains("bo.java.vm")) { fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className); } if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) { fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); } else if (template.contains("mapper.java.vm")) { fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); } else if (template.contains("service.java.vm")) { fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); } else if (template.contains("serviceImpl.java.vm")) { fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); } else if (template.contains("controller.java.vm")) { fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); } else if (template.contains("mapper.xml.vm")) { fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); } else if (template.contains("sql.vm")) { fileName = businessName + "Menu.sql"; } else if (template.contains("api.js.vm")) { fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); } else if (template.contains("index.vue.vm")) { fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); } else if (template.contains("index-tree.vue.vm")) { fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); } return fileName; } /** * 获取包前缀 * * @param packageName 包名称 * @return 包前缀名称 */ public static String getPackagePrefix(String packageName) { int lastIndex = packageName.lastIndexOf("."); return StringUtils.substring(packageName, 0, lastIndex); } /** * 根据列类型获取导入包 * * @param genTable 业务表对象 * @return 返回需要导入的包列表 */ public static HashSet getImportList(GenTable genTable) { List columns = genTable.getColumns(); GenTable subGenTable = genTable.getSubTable(); HashSet importList = new HashSet<>(); if (ObjectUtil.isNotNull(subGenTable)) { importList.add("java.util.List"); } for (GenTableColumn column : columns) { if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) { importList.add("java.util.Date"); importList.add("com.fasterxml.jackson.annotation.JsonFormat"); } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) { importList.add("java.math.BigDecimal"); } } return importList; } /** * 根据列类型获取字典组 * * @param genTable 业务表对象 * @return 返回字典组 */ public static String getDicts(GenTable genTable) { List columns = genTable.getColumns(); Set dicts = new HashSet(); addDicts(dicts, columns); if (ObjectUtil.isNotNull(genTable.getSubTable())) { List subColumns = genTable.getSubTable().getColumns(); addDicts(dicts, subColumns); } return StringUtils.join(dicts, ", "); } /** * 添加字典列表 * * @param dicts 字典列表 * @param columns 列集合 */ public static void addDicts(Set dicts, List columns) { for (GenTableColumn column : columns) { if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( column.getHtmlType(), new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) { dicts.add("'" + column.getDictType() + "'"); } } } /** * 获取权限前缀 * * @param moduleName 模块名称 * @param businessName 业务名称 * @return 返回权限前缀 */ public static String getPermissionPrefix(String moduleName, String businessName) { return StringUtils.format("{}:{}", moduleName, businessName); } /** * 获取上级菜单ID字段 * * @param paramsObj 生成其他选项 * @return 上级菜单ID字段 */ public static String getParentMenuId(Dict paramsObj) { if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) && StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) { return paramsObj.getStr(GenConstants.PARENT_MENU_ID); } return DEFAULT_PARENT_MENU_ID; } /** * 获取树编码 * * @param paramsObj 生成其他选项 * @return 树编码 */ public static String getTreecode(Map paramsObj) { if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) { return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE))); } return StringUtils.EMPTY; } /** * 获取树父编码 * * @param paramsObj 生成其他选项 * @return 树父编码 */ public static String getTreeParentCode(Dict paramsObj) { if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE)); } return StringUtils.EMPTY; } /** * 获取树名称 * * @param paramsObj 生成其他选项 * @return 树名称 */ public static String getTreeName(Dict paramsObj) { if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) { return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME)); } return StringUtils.EMPTY; } /** * 获取需要在哪一列上面显示展开按钮 * * @param genTable 业务表对象 * @return 展开按钮列序号 */ public static int getExpandColumn(GenTable genTable) { String options = genTable.getOptions(); Dict paramsObj = JsonUtils.parseMap(options); String treeName = paramsObj.getStr(GenConstants.TREE_NAME); int num = 0; for (GenTableColumn column : genTable.getColumns()) { if (column.isList()) { num++; String columnName = column.getColumnName(); if (columnName.equals(treeName)) { break; } } } return num; } } ================================================ FILE: ruoyi-generator/src/main/resources/generator.yml ================================================ # 代码生成 gen: # 作者 author: ruoyi # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool packageName: top.flya.system # 自动去除表前缀,默认是false autoRemovePre: false # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) tablePrefix: sys_ ================================================ FILE: ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml ================================================ ================================================ FILE: ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml ================================================ ================================================ FILE: ruoyi-generator/src/main/resources/mapper/package-info.md ================================================ java包使用 `.` 分割 resource 目录使用 `/` 分割
        此文件目的 防止文件夹粘连找不到 `xml` 文件 ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/bo.java.vm ================================================ package ${packageName}.domain.bo; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.*; #foreach ($import in $importList) import ${import}; #end #if($table.crud || $table.sub) #elseif($table.tree) #end /** * ${functionName}业务对象 ${tableName} * * @author ${author} * @date ${datetime} */ #if($table.crud || $table.sub) #set($Entity="BaseEntity") #elseif($table.tree) #set($Entity="TreeEntity<${ClassName}Bo>") #end @Data @EqualsAndHashCode(callSuper = true) public class ${ClassName}Bo extends ${Entity} { #foreach ($column in $columns) #if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.insert || $column.edit)) /** * $column.columnComment */ #if($column.insert && $column.edit) #set($Group="AddGroup.class, EditGroup.class") #elseif($column.insert) #set($Group="AddGroup.class") #elseif($column.edit) #set($Group="EditGroup.class") #end #if($column.required) #if($column.javaType == 'String') @NotBlank(message = "$column.columnComment不能为空", groups = { $Group }) #else @NotNull(message = "$column.columnComment不能为空", groups = { $Group }) #end #end private $column.javaType $column.javaField; #end #end } ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/controller.java.vm ================================================ package ${packageName}.controller; import java.util.List; import java.util.Arrays; import lombok.RequiredArgsConstructor; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; import top.flya.common.annotation.RepeatSubmit; import top.flya.common.annotation.Log; import top.flya.common.core.controller.BaseController; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.R; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import top.flya.common.enums.BusinessType; import top.flya.common.utils.poi.ExcelUtil; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.service.I${ClassName}Service; #if($table.crud || $table.sub) import top.flya.common.core.page.TableDataInfo; #elseif($table.tree) #end /** * ${functionName} * * @author ${author} * @date ${datetime} */ @Validated @RequiredArgsConstructor @RestController @RequestMapping("/${moduleName}/${businessName}") public class ${ClassName}Controller extends BaseController { private final I${ClassName}Service i${ClassName}Service; /** * 查询${functionName}列表 */ @SaCheckPermission("${permissionPrefix}:list") @GetMapping("/list") #if($table.crud || $table.sub) public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { return i${ClassName}Service.queryPageList(bo, pageQuery); } #elseif($table.tree) public R> list(${ClassName}Bo bo) { List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo); return R.ok(list); } #end /** * 导出${functionName}列表 */ @SaCheckPermission("${permissionPrefix}:export") @Log(title = "${functionName}", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(${ClassName}Bo bo, HttpServletResponse response) { List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo); ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response); } /** * 获取${functionName}详细信息 * * @param ${pkColumn.javaField} 主键 */ @SaCheckPermission("${permissionPrefix}:query") @GetMapping("/{${pkColumn.javaField}}") public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空") @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) { return R.ok(i${ClassName}Service.queryById(${pkColumn.javaField})); } /** * 新增${functionName} */ @SaCheckPermission("${permissionPrefix}:add") @Log(title = "${functionName}", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) { return toAjax(i${ClassName}Service.insertByBo(bo)); } /** * 修改${functionName} */ @SaCheckPermission("${permissionPrefix}:edit") @Log(title = "${functionName}", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) { return toAjax(i${ClassName}Service.updateByBo(bo)); } /** * 删除${functionName} * * @param ${pkColumn.javaField}s 主键串 */ @SaCheckPermission("${permissionPrefix}:remove") @Log(title = "${functionName}", businessType = BusinessType.DELETE) @DeleteMapping("/{${pkColumn.javaField}s}") public R remove(@NotEmpty(message = "主键不能为空") @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { return toAjax(i${ClassName}Service.deleteWithValidByIds(Arrays.asList(${pkColumn.javaField}s), true)); } } ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/domain.java.vm ================================================ package ${packageName}.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; #foreach ($import in $importList) import ${import}; #end #if($table.crud || $table.sub) #elseif($table.tree) #end /** * ${functionName}对象 ${tableName} * * @author ${author} * @date ${datetime} */ #if($table.crud || $table.sub) #set($Entity="BaseEntity") #elseif($table.tree) #set($Entity="TreeEntity<${ClassName}>") #end @Data @EqualsAndHashCode(callSuper = true) @TableName("${tableName}") public class ${ClassName} extends ${Entity} { private static final long serialVersionUID=1L; #foreach ($column in $columns) #if(!$table.isSuperColumn($column.javaField)) /** * $column.columnComment */ #if($column.javaField=='delFlag') @TableLogic #end #if($column.javaField=='version') @Version #end #if($column.pk) @TableId(value = "$column.columnName") #end private $column.javaType $column.javaField; #end #end } ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/mapper.java.vm ================================================ package ${packageName}.mapper; import ${packageName}.domain.${ClassName}; import ${packageName}.domain.vo.${ClassName}Vo; import top.flya.common.core.mapper.BaseMapperPlus; /** * ${functionName}Mapper接口 * * @author ${author} * @date ${datetime} */ public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}Mapper, ${ClassName}, ${ClassName}Vo> { } ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/service.java.vm ================================================ package ${packageName}.service; import ${packageName}.domain.${ClassName}; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; #if($table.crud || $table.sub) import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; #end import java.util.Collection; import java.util.List; /** * ${functionName}Service接口 * * @author ${author} * @date ${datetime} */ public interface I${ClassName}Service { /** * 查询${functionName} */ ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); #if($table.crud || $table.sub) /** * 查询${functionName}列表 */ TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery); #end /** * 查询${functionName}列表 */ List<${ClassName}Vo> queryList(${ClassName}Bo bo); /** * 新增${functionName} */ Boolean insertByBo(${ClassName}Bo bo); /** * 修改${functionName} */ Boolean updateByBo(${ClassName}Bo bo); /** * 校验并批量删除${functionName}信息 */ Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid); } ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm ================================================ package ${packageName}.service.impl; import cn.hutool.core.bean.BeanUtil; #if($table.crud || $table.sub) import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; #end import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.${ClassName}; import ${packageName}.mapper.${ClassName}Mapper; import ${packageName}.service.I${ClassName}Service; import java.util.List; import java.util.Map; import java.util.Collection; /** * ${functionName}Service业务层处理 * * @author ${author} * @date ${datetime} */ @RequiredArgsConstructor @Service public class ${ClassName}ServiceImpl implements I${ClassName}Service { private final ${ClassName}Mapper baseMapper; /** * 查询${functionName} */ @Override public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){ return baseMapper.selectVoById(${pkColumn.javaField}); } #if($table.crud || $table.sub) /** * 查询${functionName}列表 */ @Override public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) { LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } #end /** * 查询${functionName}列表 */ @Override public List<${ClassName}Vo> queryList(${ClassName}Bo bo) { LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) { Map params = bo.getParams(); LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery(); #foreach($column in $columns) #if($column.query) #set($queryType=$column.queryType) #set($javaField=$column.javaField) #set($javaType=$column.javaType) #set($columnName=$column.columnName) #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) #set($mpMethod=$column.queryType.toLowerCase()) #if($queryType != 'BETWEEN') #if($javaType == 'String') #set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())') #else #set($condition='bo.get'+$AttrName+'() != null') #end lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()); #else lqw.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null, ${ClassName}::get$AttrName ,params.get("begin$AttrName"), params.get("end$AttrName")); #end #end #end return lqw; } /** * 新增${functionName} */ @Override public Boolean insertByBo(${ClassName}Bo bo) { ${ClassName} add = BeanUtil.toBean(bo, ${ClassName}.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; #set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)}) if (flag) { bo.set$pk(add.get$pk()); } return flag; } /** * 修改${functionName} */ @Override public Boolean updateByBo(${ClassName}Bo bo) { ${ClassName} update = BeanUtil.toBean(bo, ${ClassName}.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(${ClassName} entity){ //TODO 做一些数据校验,如唯一约束 } /** * 批量删除${functionName} */ @Override public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteBatchIds(ids) > 0; } } ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm ================================================ package ${packageName}.domain; #foreach ($import in $subImportList) import ${import}; #end import top.flya.common.annotation.Excel; /** * ${subTable.functionName}对象 ${subTableName} * * @author ${author} * @date ${datetime} */ public class ${subClassName} extends BaseEntity { private static final long serialVersionUID = 1L; #foreach ($column in $subTable.columns) #if(!$table.isSuperColumn($column.javaField)) /** $column.columnComment */ #if($column.list) #set($parentheseIndex=$column.columnComment.indexOf("(")) #if($parentheseIndex != -1) #set($comment=$column.columnComment.substring(0, $parentheseIndex)) #else #set($comment=$column.columnComment) #end #if($parentheseIndex != -1) @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") #elseif($column.javaType == 'Date') @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") #else @Excel(name = "${comment}") #end #end private $column.javaType $column.javaField; #end #end #foreach ($column in $subTable.columns) #if(!$table.isSuperColumn($column.javaField)) #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) #set($AttrName=$column.javaField) #else #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) #end public void set${AttrName}($column.javaType $column.javaField) { this.$column.javaField = $column.javaField; } public $column.javaType get${AttrName}() { return $column.javaField; } #end #end @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) #foreach ($column in $subTable.columns) #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) #set($AttrName=$column.javaField) #else #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) #end .append("${column.javaField}", get${AttrName}()) #end .toString(); } } ================================================ FILE: ruoyi-generator/src/main/resources/vm/java/vo.java.vm ================================================ package ${packageName}.domain.vo; #foreach ($import in $importList) import ${import}; #end import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; /** * ${functionName}视图对象 ${tableName} * * @author ${author} * @date ${datetime} */ @Data @ExcelIgnoreUnannotated public class ${ClassName}Vo { private static final long serialVersionUID = 1L; #foreach ($column in $columns) #if($column.list) /** * $column.columnComment */ #set($parentheseIndex=$column.columnComment.indexOf("(")) #if($parentheseIndex != -1) #set($comment=$column.columnComment.substring(0, $parentheseIndex)) #else #set($comment=$column.columnComment) #end #if(${column.dictType} && ${column.dictType} != '') @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "${column.dictType}") #elseif($parentheseIndex != -1) @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) @ExcelDictFormat(readConverterExp = "$column.readConverterExp()") #else @ExcelProperty(value = "${comment}") #end private $column.javaType $column.javaField; #end #end } ================================================ FILE: ruoyi-generator/src/main/resources/vm/js/api.js.vm ================================================ import request from '@/utils/request' // 查询${functionName}列表 export function list${BusinessName}(query) { return request({ url: '/${moduleName}/${businessName}/list', method: 'get', params: query }) } // 查询${functionName}详细 export function get${BusinessName}(${pkColumn.javaField}) { return request({ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, method: 'get' }) } // 新增${functionName} export function add${BusinessName}(data) { return request({ url: '/${moduleName}/${businessName}', method: 'post', data: data }) } // 修改${functionName} export function update${BusinessName}(data) { return request({ url: '/${moduleName}/${businessName}', method: 'put', data: data }) } // 删除${functionName} export function del${BusinessName}(${pkColumn.javaField}) { return request({ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, method: 'delete' }) } ================================================ FILE: ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm ================================================ -- 菜单 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate, '', null, '${functionName}菜单'); -- 按钮 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate, '', null, ''); ================================================ FILE: ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm ================================================ -- 菜单 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', now(), '', null, '${functionName}菜单'); -- 按钮 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', now(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', now(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', now(), '', null, ''); ================================================ FILE: ruoyi-generator/src/main/resources/vm/sql/sql.vm ================================================ -- 菜单 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); -- 按钮 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); ================================================ FILE: ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm ================================================ -- 菜单 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', getdate(), '', null, '${functionName}菜单'); -- 按钮 SQL insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', getdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', getdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', getdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', getdate(), '', null, ''); insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', getdate(), '', null, ''); ================================================ FILE: ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm ================================================ ================================================ FILE: ruoyi-generator/src/main/resources/vm/vue/index.vue.vm ================================================ ================================================ FILE: ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm ================================================ ================================================ FILE: ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm ================================================ ================================================ FILE: ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt ================================================ 如果使用的是Vue3前端,那么需要覆盖一下此目录的模板index.vue.vm、index-tree.vue.vm文件到上级vue目录。 ================================================ FILE: ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm ================================================ #foreach ($column in $columns) #end ================================================ FILE: ruoyi-job/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 jar ruoyi-job 任务调度 com.ruoyi ruoyi-common com.xuxueli xxl-job-core ================================================ FILE: ruoyi-job/src/main/java/top/flya/job/config/XxlJobConfig.java ================================================ package top.flya.job.config; import top.flya.job.config.properties.XxlJobProperties; import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * xxl-job config * * @author Lion Li */ @Slf4j @Configuration @EnableConfigurationProperties(XxlJobProperties.class) @AllArgsConstructor @ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true") public class XxlJobConfig { private final XxlJobProperties xxlJobProperties; @Bean public XxlJobSpringExecutor xxlJobExecutor() { log.info(">>>>>>>>>>> xxl-job config init."); XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses()); xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken()); XxlJobProperties.Executor executor = xxlJobProperties.getExecutor(); xxlJobSpringExecutor.setAppname(executor.getAppname()); xxlJobSpringExecutor.setAddress(executor.getAddress()); xxlJobSpringExecutor.setIp(executor.getIp()); xxlJobSpringExecutor.setPort(executor.getPort()); xxlJobSpringExecutor.setLogPath(executor.getLogPath()); xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays()); return xxlJobSpringExecutor; } } ================================================ FILE: ruoyi-job/src/main/java/top/flya/job/config/properties/XxlJobProperties.java ================================================ package top.flya.job.config.properties; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.ConfigurationProperties; /** * xxljob配置类 * * @author Lion Li */ @Data @ConfigurationProperties(prefix = "xxl.job") public class XxlJobProperties { private Boolean enabled; private String adminAddresses; private String accessToken; private Executor executor; @Data @NoArgsConstructor public static class Executor { private String appname; private String address; private String ip; private int port; private String logPath; private int logRetentionDays; } } ================================================ FILE: ruoyi-job/src/main/java/top/flya/job/service/SampleService.java ================================================ package top.flya.job.service; import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Arrays; /** * XxlJob开发示例(Bean模式) *

        * 开发步骤: * 1、任务开发:在Spring Bean实例中,开发Job方法; * 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。 * 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志; * 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果; * * @author xuxueli 2019-12-11 21:52:51 */ @Slf4j @Service public class SampleService { /** * 1、简单任务示例(Bean模式) */ @XxlJob("demoJobHandler") public void demoJobHandler() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World."); for (int i = 0; i < 5; i++) { XxlJobHelper.log("beat at:" + i); } // default success } /** * 2、分片广播任务 */ @XxlJob("shardingJobHandler") public void shardingJobHandler() throws Exception { // 分片参数 int shardIndex = XxlJobHelper.getShardIndex(); int shardTotal = XxlJobHelper.getShardTotal(); XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); // 业务逻辑 for (int i = 0; i < shardTotal; i++) { if (i == shardIndex) { XxlJobHelper.log("第 {} 片, 命中分片开始处理", i); } else { XxlJobHelper.log("第 {} 片, 忽略", i); } } } /** * 3、命令行任务 */ @XxlJob("commandJobHandler") public void commandJobHandler() throws Exception { String command = XxlJobHelper.getJobParam(); int exitValue = -1; BufferedReader bufferedReader = null; try { // command process ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.command(command); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); //Process process = Runtime.getRuntime().exec(command); BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); // command log String line; while ((line = bufferedReader.readLine()) != null) { XxlJobHelper.log(line); } // command exit process.waitFor(); exitValue = process.exitValue(); } catch (Exception e) { XxlJobHelper.log(e); } finally { if (bufferedReader != null) { bufferedReader.close(); } } if (exitValue == 0) { // default success } else { XxlJobHelper.handleFail("command exit value(" + exitValue + ") is failed"); } } /** * 4、跨平台Http任务 * 参数示例: * "url: http://www.baidu.com\n" + * "method: get\n" + * "data: content\n"; */ @XxlJob("httpJobHandler") public void httpJobHandler() throws Exception { // param parse String param = XxlJobHelper.getJobParam(); if (param == null || param.trim().length() == 0) { XxlJobHelper.log("param[" + param + "] invalid."); XxlJobHelper.handleFail(); return; } String[] httpParams = param.split("\n"); String url = null; String method = null; String data = null; for (String httpParam : httpParams) { if (httpParam.startsWith("url:")) { url = httpParam.substring(httpParam.indexOf("url:") + 4).trim(); } if (httpParam.startsWith("method:")) { method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase(); } if (httpParam.startsWith("data:")) { data = httpParam.substring(httpParam.indexOf("data:") + 5).trim(); } } // param valid if (url == null || url.trim().length() == 0) { XxlJobHelper.log("url[" + url + "] invalid."); XxlJobHelper.handleFail(); return; } if (method == null || !Arrays.asList("GET", "POST").contains(method)) { XxlJobHelper.log("method[" + method + "] invalid."); XxlJobHelper.handleFail(); return; } boolean isPostMethod = method.equals("POST"); // request HttpURLConnection connection = null; BufferedReader bufferedReader = null; try { // connection URL realUrl = new URL(url); connection = (HttpURLConnection) realUrl.openConnection(); // connection setting connection.setRequestMethod(method); connection.setDoOutput(isPostMethod); connection.setDoInput(true); connection.setUseCaches(false); connection.setReadTimeout(5 * 1000); connection.setConnectTimeout(3 * 1000); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); // do connection connection.connect(); // data if (isPostMethod && data != null && data.trim().length() > 0) { DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); dataOutputStream.write(data.getBytes("UTF-8")); dataOutputStream.flush(); dataOutputStream.close(); } // valid StatusCode int statusCode = connection.getResponseCode(); if (statusCode != 200) { throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid."); } // result bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); StringBuilder result = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { result.append(line); } String responseMsg = result.toString(); XxlJobHelper.log(responseMsg); return; } catch (Exception e) { XxlJobHelper.log(e); XxlJobHelper.handleFail(); return; } finally { try { if (bufferedReader != null) { bufferedReader.close(); } if (connection != null) { connection.disconnect(); } } catch (Exception e2) { XxlJobHelper.log(e2); } } } /** * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑; */ @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") public void demoJobHandler2() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World."); } public void init() { log.info("init"); } public void destroy() { log.info("destory"); } } ================================================ FILE: ruoyi-oss/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 ruoyi-oss OSS对象存储模块 com.ruoyi ruoyi-common com.amazonaws aws-java-sdk-s3 ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/constant/OssConstant.java ================================================ package top.flya.oss.constant; import java.util.Arrays; import java.util.List; /** * 对象存储常量 * * @author Lion Li */ public interface OssConstant { /** * 默认配置KEY */ String DEFAULT_CONFIG_KEY = "sys_oss:default_config"; /** * 预览列表资源开关Key */ String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource"; /** * 系统数据ids */ List SYSTEM_DATA_IDS = Arrays.asList(1L, 2L, 3L, 4L); /** * 云服务商 */ String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu", "obs"}; /** * https 状态 */ String IS_HTTPS = "Y"; } ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/core/OssClient.java ================================================ package top.flya.oss.core; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.IdUtil; import com.amazonaws.ClientConfiguration; import com.amazonaws.HttpMethod; import com.amazonaws.Protocol; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.*; import top.flya.common.utils.DateUtils; import top.flya.common.utils.StringUtils; import top.flya.oss.constant.OssConstant; import top.flya.oss.entity.UploadResult; import top.flya.oss.enumd.AccessPolicyType; import top.flya.oss.enumd.PolicyType; import top.flya.oss.exception.OssException; import top.flya.oss.properties.OssProperties; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URL; import java.util.Date; /** * S3 存储协议 所有兼容S3协议的云厂商均支持 * 阿里云 腾讯云 七牛云 minio * * @author Lion Li */ public class OssClient { private final String configKey; private final OssProperties properties; private final AmazonS3 client; public OssClient(String configKey, OssProperties ossProperties) { this.configKey = configKey; this.properties = ossProperties; try { AwsClientBuilder.EndpointConfiguration endpointConfig = new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion()); AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey()); AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials); ClientConfiguration clientConfig = new ClientConfiguration(); if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) { clientConfig.setProtocol(Protocol.HTTPS); } else { clientConfig.setProtocol(Protocol.HTTP); } AmazonS3ClientBuilder build = AmazonS3Client.builder() .withEndpointConfiguration(endpointConfig) .withClientConfiguration(clientConfig) .withCredentials(credentialsProvider) .disableChunkedEncoding(); if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) { // minio 使用https限制使用域名访问 需要此配置 站点填域名 build.enablePathStyleAccess(); } this.client = build.build(); createBucket(); } catch (Exception e) { if (e instanceof OssException) { throw e; } throw new OssException("配置错误! 请检查系统配置:[" + e.getMessage() + "]"); } } public void createBucket() { try { String bucketName = properties.getBucketName(); if (client.doesBucketExistV2(bucketName)) { return; } CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName); AccessPolicyType accessPolicy = getAccessPolicy(); createBucketRequest.setCannedAcl(accessPolicy.getAcl()); client.createBucket(createBucketRequest); client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType())); } catch (Exception e) { throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]"); } } public UploadResult upload(byte[] data, String path, String contentType) { return upload(new ByteArrayInputStream(data), path, contentType); } public UploadResult upload(InputStream inputStream, String path, String contentType) { if (!(inputStream instanceof ByteArrayInputStream)) { inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream)); } try { ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentType(contentType); metadata.setContentLength(inputStream.available()); PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata); // 设置上传对象的 Acl 为公共读 putObjectRequest.setCannedAcl(getAccessPolicy().getAcl()); client.putObject(putObjectRequest); } catch (Exception e) { throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]"); } return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build(); } public void delete(String path) { path = path.replace(getUrl() + "/", ""); try { client.deleteObject(properties.getBucketName(), path); } catch (Exception e) { throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]"); } } public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) { return upload(data, getPath(properties.getPrefix(), suffix), contentType); } public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) { return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType); } /** * 获取文件元数据 * * @param path 完整文件路径 */ public ObjectMetadata getObjectMetadata(String path) { path = path.replace(getUrl() + "/", ""); S3Object object = client.getObject(properties.getBucketName(), path); return object.getObjectMetadata(); } public InputStream getObjectContent(String path) { path = path.replace(getUrl() + "/", ""); S3Object object = client.getObject(properties.getBucketName(), path); return object.getObjectContent(); } public String getUrl() { String domain = properties.getDomain(); String endpoint = properties.getEndpoint(); String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://"; // 云服务商直接返回 if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { if (StringUtils.isNotBlank(domain)) { return header + domain; } return header + properties.getBucketName() + "." + endpoint; } // minio 单独处理 if (StringUtils.isNotBlank(domain)) { return header + domain + "/" + properties.getBucketName(); } return header + endpoint + "/" + properties.getBucketName(); } public String getPath(String prefix, String suffix) { // 生成uuid String uuid = IdUtil.fastSimpleUUID(); // 文件路径 String path = DateUtils.datePath() + "/" + uuid; if (StringUtils.isNotBlank(prefix)) { path = prefix + "/" + path; } return path + suffix; } public String getConfigKey() { return configKey; } /** * 获取私有URL链接 * * @param objectKey 对象KEY * @param second 授权时间 */ public String getPrivateUrl(String objectKey, Integer second) { GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey) .withMethod(HttpMethod.GET) .withExpiration(new Date(System.currentTimeMillis() + 1000L * second)); URL url = client.generatePresignedUrl(generatePresignedUrlRequest); return url.toString(); } /** * 检查配置是否相同 */ public boolean checkPropertiesSame(OssProperties properties) { return this.properties.equals(properties); } /** * 获取当前桶权限类型 * * @return 当前桶权限类型code */ public AccessPolicyType getAccessPolicy() { return AccessPolicyType.getByType(properties.getAccessPolicy()); } private static String getPolicy(String bucketName, PolicyType policyType) { StringBuilder builder = new StringBuilder(); builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n"); if (policyType == PolicyType.WRITE) { builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n"); } else if (policyType == PolicyType.READ_WRITE) { builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n"); } else { builder.append("\"s3:GetBucketLocation\"\n"); } builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); builder.append(bucketName); builder.append("\"\n},\n"); if (policyType == PolicyType.READ) { builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); builder.append(bucketName); builder.append("\"\n},\n"); } builder.append("{\n\"Action\": "); switch (policyType) { case WRITE: builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"); break; case READ_WRITE: builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"); break; default: builder.append("\"s3:GetObject\",\n"); break; } builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); builder.append(bucketName); builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n"); return builder.toString(); } } ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/entity/UploadResult.java ================================================ package top.flya.oss.entity; import lombok.Builder; import lombok.Data; /** * 上传返回体 * * @author Lion Li */ @Data @Builder public class UploadResult { /** * 文件路径 */ private String url; /** * 文件名 */ private String filename; } ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/enumd/AccessPolicyType.java ================================================ package top.flya.oss.enumd; import com.amazonaws.services.s3.model.CannedAccessControlList; import lombok.AllArgsConstructor; import lombok.Getter; /** * 桶访问策略配置 * * @author 陈賝 */ @Getter @AllArgsConstructor public enum AccessPolicyType { /** * private */ PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE), /** * public */ PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ), /** * custom */ CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ); /** * 桶 权限类型 */ private final String type; /** * 文件对象 权限类型 */ private final CannedAccessControlList acl; /** * 桶策略类型 */ private final PolicyType policyType; public static AccessPolicyType getByType(String type) { for (AccessPolicyType value : values()) { if (value.getType().equals(type)) { return value; } } throw new RuntimeException("'type' not found By " + type); } } ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/enumd/PolicyType.java ================================================ package top.flya.oss.enumd; import lombok.AllArgsConstructor; import lombok.Getter; /** * minio策略配置 * * @author Lion Li */ @Getter @AllArgsConstructor public enum PolicyType { /** * 只读 */ READ("read-only"), /** * 只写 */ WRITE("write-only"), /** * 读写 */ READ_WRITE("read-write"); /** * 类型 */ private final String type; } ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/exception/OssException.java ================================================ package top.flya.oss.exception; /** * OSS异常类 * * @author Lion Li */ public class OssException extends RuntimeException { private static final long serialVersionUID = 1L; public OssException(String msg) { super(msg); } } ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/factory/OssFactory.java ================================================ package top.flya.oss.factory; import top.flya.common.constant.CacheNames; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.CacheUtils; import top.flya.common.utils.redis.RedisUtils; import top.flya.oss.constant.OssConstant; import top.flya.oss.core.OssClient; import top.flya.oss.exception.OssException; import top.flya.oss.properties.OssProperties; import lombok.extern.slf4j.Slf4j; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 文件上传Factory * * @author Lion Li */ @Slf4j public class OssFactory { private static final Map CLIENT_CACHE = new ConcurrentHashMap<>(); /** * 获取默认实例 */ public static OssClient instance() { // 获取redis 默认类型 String configKey = RedisUtils.getCacheObject(OssConstant.DEFAULT_CONFIG_KEY); if (StringUtils.isEmpty(configKey)) { throw new OssException("文件存储服务类型无法找到!"); } return instance(configKey); } /** * 根据类型获取实例 */ public static OssClient instance(String configKey) { String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); if (json == null) { throw new OssException("系统异常, '" + configKey + "'配置信息不存在!"); } OssProperties properties = JsonUtils.parseObject(json, OssProperties.class); OssClient client = CLIENT_CACHE.get(configKey); if (client == null) { CLIENT_CACHE.put(configKey, new OssClient(configKey, properties)); log.info("创建OSS实例 key => {}", configKey); return CLIENT_CACHE.get(configKey); } // 配置不相同则重新构建 if (!client.checkPropertiesSame(properties)) { CLIENT_CACHE.put(configKey, new OssClient(configKey, properties)); log.info("重载OSS实例 key => {}", configKey); return CLIENT_CACHE.get(configKey); } return client; } } ================================================ FILE: ruoyi-oss/src/main/java/top/flya/oss/properties/OssProperties.java ================================================ package top.flya.oss.properties; import lombok.Data; /** * OSS对象存储 配置属性 * * @author Lion Li */ @Data public class OssProperties { /** * 访问站点 */ private String endpoint; /** * 自定义域名 */ private String domain; /** * 前缀 */ private String prefix; /** * ACCESS_KEY */ private String accessKey; /** * SECRET_KEY */ private String secretKey; /** * 存储空间名 */ private String bucketName; /** * 存储区域 */ private String region; /** * 是否https(Y=是,N=否) */ private String isHttps; /** * 桶权限类型(0private 1public 2custom) */ private String accessPolicy; } ================================================ FILE: ruoyi-sms/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 ruoyi-sms SMS短信模块 com.ruoyi ruoyi-common com.aliyun dysmsapi20170525 true com.tencentcloudapi tencentcloud-sdk-java-sms true ================================================ FILE: ruoyi-sms/src/main/java/top/flya/sms/config/SmsConfig.java ================================================ package top.flya.sms.config; import top.flya.sms.config.properties.SmsProperties; import top.flya.sms.core.AliyunSmsTemplate; import top.flya.sms.core.SmsTemplate; import top.flya.sms.core.TencentSmsTemplate; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 短信配置类 * * @author Lion Li * @version 4.2.0 */ @Configuration public class SmsConfig { @Configuration @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class) static class AliyunSmsConfig { @Bean public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) { return new AliyunSmsTemplate(smsProperties); } } @Configuration @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class) static class TencentSmsConfig { @Bean public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) { return new TencentSmsTemplate(smsProperties); } } } ================================================ FILE: ruoyi-sms/src/main/java/top/flya/sms/config/properties/SmsProperties.java ================================================ package top.flya.sms.config.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * SMS短信 配置属性 * * @author Lion Li * @version 4.2.0 */ @Data @Component @ConfigurationProperties(prefix = "sms") public class SmsProperties { private Boolean enabled; /** * 配置节点 * 阿里云 dysmsapi.aliyuncs.com * 腾讯云 sms.tencentcloudapi.com */ private String endpoint; /** * key */ private String accessKeyId; /** * 密匙 */ private String accessKeySecret; /* * 短信签名 */ private String signName; /** * 短信应用ID (腾讯专属) */ private String sdkAppId; } ================================================ FILE: ruoyi-sms/src/main/java/top/flya/sms/core/AliyunSmsTemplate.java ================================================ package top.flya.sms.core; import com.aliyun.dysmsapi20170525.Client; import com.aliyun.dysmsapi20170525.models.SendSmsRequest; import com.aliyun.dysmsapi20170525.models.SendSmsResponse; import com.aliyun.teaopenapi.models.Config; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StringUtils; import top.flya.sms.config.properties.SmsProperties; import top.flya.sms.entity.SmsResult; import top.flya.sms.exception.SmsException; import lombok.SneakyThrows; import java.util.Map; /** * Aliyun 短信模板 * * @author Lion Li * @version 4.2.0 */ public class AliyunSmsTemplate implements SmsTemplate { private SmsProperties properties; private Client client; @SneakyThrows(Exception.class) public AliyunSmsTemplate(SmsProperties smsProperties) { this.properties = smsProperties; Config config = new Config() // 您的AccessKey ID .setAccessKeyId(smsProperties.getAccessKeyId()) // 您的AccessKey Secret .setAccessKeySecret(smsProperties.getAccessKeySecret()) // 访问的域名 .setEndpoint(smsProperties.getEndpoint()); this.client = new Client(config); } @Override public SmsResult send(String phones, String templateId, Map param) { if (StringUtils.isBlank(phones)) { throw new SmsException("手机号不能为空"); } if (StringUtils.isBlank(templateId)) { throw new SmsException("模板ID不能为空"); } SendSmsRequest req = new SendSmsRequest() .setPhoneNumbers(phones) .setSignName(properties.getSignName()) .setTemplateCode(templateId) .setTemplateParam(JsonUtils.toJsonString(param)); try { SendSmsResponse resp = client.sendSms(req); return SmsResult.builder() .isSuccess("OK".equals(resp.getBody().getCode())) .message(resp.getBody().getMessage()) .response(JsonUtils.toJsonString(resp)) .build(); } catch (Exception e) { throw new SmsException(e.getMessage()); } } } ================================================ FILE: ruoyi-sms/src/main/java/top/flya/sms/core/SmsTemplate.java ================================================ package top.flya.sms.core; import top.flya.sms.entity.SmsResult; import java.util.Map; /** * 短信模板 * * @author Lion Li * @version 4.2.0 */ public interface SmsTemplate { /** * 发送短信 * * @param phones 电话号(多个逗号分割) * @param templateId 模板id * @param param 模板对应参数 * 阿里 需使用 模板变量名称对应内容 例如: code=1234 * 腾讯 需使用 模板变量顺序对应内容 例如: 1=1234, 1为模板内第一个参数 */ SmsResult send(String phones, String templateId, Map param); } ================================================ FILE: ruoyi-sms/src/main/java/top/flya/sms/core/TencentSmsTemplate.java ================================================ package top.flya.sms.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StringUtils; import top.flya.sms.config.properties.SmsProperties; import top.flya.sms.entity.SmsResult; import top.flya.sms.exception.SmsException; import com.tencentcloudapi.common.Credential; import com.tencentcloudapi.common.profile.ClientProfile; import com.tencentcloudapi.common.profile.HttpProfile; import com.tencentcloudapi.sms.v20190711.SmsClient; import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse; import com.tencentcloudapi.sms.v20190711.models.SendStatus; import lombok.SneakyThrows; import java.util.Arrays; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * Tencent 短信模板 * * @author Lion Li * @version 4.2.0 */ public class TencentSmsTemplate implements SmsTemplate { private SmsProperties properties; private SmsClient client; @SneakyThrows(Exception.class) public TencentSmsTemplate(SmsProperties smsProperties) { this.properties = smsProperties; Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret()); HttpProfile httpProfile = new HttpProfile(); httpProfile.setEndpoint(smsProperties.getEndpoint()); ClientProfile clientProfile = new ClientProfile(); clientProfile.setHttpProfile(httpProfile); this.client = new SmsClient(credential, "", clientProfile); } @Override public SmsResult send(String phones, String templateId, Map param) { if (StringUtils.isBlank(phones)) { throw new SmsException("手机号不能为空"); } if (StringUtils.isBlank(templateId)) { throw new SmsException("模板ID不能为空"); } SendSmsRequest req = new SendSmsRequest(); Set set = Arrays.stream(phones.split(StringUtils.SEPARATOR)).map(p -> "+86" + p).collect(Collectors.toSet()); req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class)); if (CollUtil.isNotEmpty(param)) { req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class)); } req.setTemplateID(templateId); req.setSign(properties.getSignName()); req.setSmsSdkAppid(properties.getSdkAppId()); try { SendSmsResponse resp = client.SendSms(req); SmsResult.SmsResultBuilder builder = SmsResult.builder() .isSuccess(true) .message("send success") .response(JsonUtils.toJsonString(resp)); for (SendStatus sendStatus : resp.getSendStatusSet()) { if (!"Ok".equals(sendStatus.getCode())) { builder.isSuccess(false).message(sendStatus.getMessage()); break; } } return builder.build(); } catch (Exception e) { throw new SmsException(e.getMessage()); } } } ================================================ FILE: ruoyi-sms/src/main/java/top/flya/sms/entity/SmsResult.java ================================================ package top.flya.sms.entity; import lombok.Builder; import lombok.Data; /** * 上传返回体 * * @author Lion Li */ @Data @Builder public class SmsResult { /** * 是否成功 */ private boolean isSuccess; /** * 响应消息 */ private String message; /** * 实际响应体 *

        * 可自行转换为 SDK 对应的 SendSmsResponse */ private String response; } ================================================ FILE: ruoyi-sms/src/main/java/top/flya/sms/exception/SmsException.java ================================================ package top.flya.sms.exception; /** * Sms异常类 * * @author Lion Li */ public class SmsException extends RuntimeException { private static final long serialVersionUID = 1L; public SmsException(String msg) { super(msg); } } ================================================ FILE: ruoyi-system/pom.xml ================================================ ruoyi-vue-plus com.ruoyi 4.7.0 4.0.0 ruoyi-system system系统模块 com.ruoyi ruoyi-common com.ruoyi ruoyi-oss com.ruoyi ruoyi-sms ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysCache.java ================================================ package top.flya.system.domain; import top.flya.common.utils.StringUtils; import lombok.Data; import lombok.NoArgsConstructor; /** * 缓存信息 * * @author Lion Li */ @Data @NoArgsConstructor public class SysCache { /** * 缓存名称 */ private String cacheName = ""; /** * 缓存键名 */ private String cacheKey = ""; /** * 缓存内容 */ private String cacheValue = ""; /** * 备注 */ private String remark = ""; 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; } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysConfig.java ================================================ package top.flya.system.domain; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; /** * 参数配置表 sys_config * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_config") @ExcelIgnoreUnannotated public class SysConfig extends BaseEntity { /** * 参数主键 */ @ExcelProperty(value = "参数主键") @TableId(value = "config_id") private Long configId; /** * 参数名称 */ @ExcelProperty(value = "参数名称") @NotBlank(message = "参数名称不能为空") @Size(min = 0, max = 100, message = "参数名称不能超过{max}个字符") private String configName; /** * 参数键名 */ @ExcelProperty(value = "参数键名") @NotBlank(message = "参数键名长度不能为空") @Size(min = 0, max = 100, message = "参数键名长度不能超过{max}个字符") private String configKey; /** * 参数键值 */ @ExcelProperty(value = "参数键值") @NotBlank(message = "参数键值不能为空") @Size(min = 0, max = 500, message = "参数键值长度不能超过{max}个字符") private String configValue; /** * 系统内置(Y是 N否) */ @ExcelProperty(value = "系统内置", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_yes_no") private String configType; /** * 备注 */ private String remark; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysLogininfor.java ================================================ package top.flya.system.domain; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 系统访问记录表 sys_logininfor * * @author Lion Li */ @Data @TableName("sys_logininfor") @ExcelIgnoreUnannotated public class SysLogininfor implements Serializable { private static final long serialVersionUID = 1L; /** * ID */ @ExcelProperty(value = "序号") @TableId(value = "info_id") private Long infoId; /** * 用户账号 */ @ExcelProperty(value = "用户账号") private String userName; /** * 登录状态 0成功 1失败 */ @ExcelProperty(value = "登录状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_common_status") private String status; /** * 登录IP地址 */ @ExcelProperty(value = "登录地址") private String ipaddr; /** * 登录地点 */ @ExcelProperty(value = "登录地点") private String loginLocation; /** * 浏览器类型 */ @ExcelProperty(value = "浏览器") private String browser; /** * 操作系统 */ @ExcelProperty(value = "操作系统") private String os; /** * 提示消息 */ @ExcelProperty(value = "提示消息") private String msg; /** * 访问时间 */ @ExcelProperty(value = "访问时间") private Date loginTime; /** * 请求参数 */ @TableField(exist = false) private Map params = new HashMap<>(); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysNotice.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.core.domain.BaseEntity; import top.flya.common.xss.Xss; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; /** * 通知公告表 sys_notice * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_notice") public class SysNotice extends BaseEntity { /** * 公告ID */ @TableId(value = "notice_id") private Long noticeId; /** * 公告标题 */ @Xss(message = "公告标题不能包含脚本字符") @NotBlank(message = "公告标题不能为空") @Size(min = 0, max = 50, message = "公告标题不能超过{max}个字符") private String noticeTitle; /** * 公告类型(1通知 2公告) */ private String noticeType; /** * 公告内容 */ private String noticeContent; /** * 公告状态(0正常 1关闭) */ private String status; /** * 备注 */ private String remark; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysOperLog.java ================================================ package top.flya.system.domain; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 操作日志记录表 oper_log * * @author Lion Li */ @Data @TableName("sys_oper_log") @ExcelIgnoreUnannotated public class SysOperLog implements Serializable { private static final long serialVersionUID = 1L; /** * 日志主键 */ @ExcelProperty(value = "日志主键") @TableId(value = "oper_id") private Long operId; /** * 操作模块 */ @ExcelProperty(value = "操作模块") private String title; /** * 业务类型(0其它 1新增 2修改 3删除) */ @ExcelProperty(value = "业务类型", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_oper_type") private Integer businessType; /** * 业务类型数组 */ @TableField(exist = false) private Integer[] businessTypes; /** * 请求方法 */ @ExcelProperty(value = "请求方法") private String method; /** * 请求方式 */ @ExcelProperty(value = "请求方式") private String requestMethod; /** * 操作类别(0其它 1后台用户 2手机端用户) */ @ExcelProperty(value = "操作类别", converter = ExcelDictConvert.class) @ExcelDictFormat(readConverterExp = "0=其它,1=后台用户,2=手机端用户") private Integer operatorType; /** * 操作人员 */ @ExcelProperty(value = "操作人员") private String operName; /** * 部门名称 */ @ExcelProperty(value = "部门名称") private String deptName; /** * 请求url */ @ExcelProperty(value = "请求地址") private String operUrl; /** * 操作地址 */ @ExcelProperty(value = "操作地址") private String operIp; /** * 操作地点 */ @ExcelProperty(value = "操作地点") private String operLocation; /** * 请求参数 */ @ExcelProperty(value = "请求参数") private String operParam; /** * 返回参数 */ @ExcelProperty(value = "返回参数") private String jsonResult; /** * 操作状态(0正常 1异常) */ @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_common_status") private Integer status; /** * 错误消息 */ @ExcelProperty(value = "错误消息") private String errorMsg; /** * 操作时间 */ @ExcelProperty(value = "操作时间") private Date operTime; /** * 请求参数 */ @TableField(exist = false) private Map params = new HashMap<>(); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysOss.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; /** * OSS对象存储对象 * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_oss") public class SysOss extends BaseEntity { /** * 对象存储主键 */ @TableId(value = "oss_id") private Long ossId; /** * 文件名 */ private String fileName; /** * 原名 */ private String originalName; /** * 文件后缀名 */ private String fileSuffix; /** * URL地址 */ private String url; /** * 服务商 */ private String service; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysOssConfig.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; /** * 对象存储配置对象 sys_oss_config * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_oss_config") public class SysOssConfig extends BaseEntity { /** * 主建 */ @TableId(value = "oss_config_id") private Long ossConfigId; /** * 配置key */ private String configKey; /** * accessKey */ private String accessKey; /** * 秘钥 */ private String secretKey; /** * 桶名称 */ private String bucketName; /** * 前缀 */ private String prefix; /** * 访问站点 */ private String endpoint; /** * 自定义域名 */ private String domain; /** * 是否https(0否 1是) */ private String isHttps; /** * 域 */ private String region; /** * 是否默认(0=是,1=否) */ private String status; /** * 扩展字段 */ private String ext1; /** * 备注 */ private String remark; /** * 桶权限类型(0private 1public 2custom) */ private String accessPolicy; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysPost.java ================================================ package top.flya.system.domain; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * 岗位表 sys_post * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) @TableName("sys_post") @ExcelIgnoreUnannotated public class SysPost extends BaseEntity { /** * 岗位序号 */ @ExcelProperty(value = "岗位序号") @TableId(value = "post_id") private Long postId; /** * 岗位编码 */ @ExcelProperty(value = "岗位编码") @NotBlank(message = "岗位编码不能为空") @Size(min = 0, max = 64, message = "岗位编码长度不能超过{max}个字符") private String postCode; /** * 岗位名称 */ @ExcelProperty(value = "岗位名称") @NotBlank(message = "岗位名称不能为空") @Size(min = 0, max = 50, message = "岗位名称长度不能超过{max}个字符") private String postName; /** * 岗位排序 */ @ExcelProperty(value = "岗位排序") @NotNull(message = "显示顺序不能为空") private Integer postSort; /** * 状态(0正常 1停用) */ @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_normal_disable") private String status; /** * 备注 */ private String remark; /** * 用户是否存在此岗位标识 默认不存在 */ @TableField(exist = false) private boolean flag = false; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysRoleDept.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * 角色和部门关联 sys_role_dept * * @author Lion Li */ @Data @TableName("sys_role_dept") public class SysRoleDept { /** * 角色ID */ @TableId(type = IdType.INPUT) private Long roleId; /** * 部门ID */ private Long deptId; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysRoleMenu.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * 角色和菜单关联 sys_role_menu * * @author Lion Li */ @Data @TableName("sys_role_menu") public class SysRoleMenu { /** * 角色ID */ @TableId(type = IdType.INPUT) private Long roleId; /** * 菜单ID */ private Long menuId; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysUserOnline.java ================================================ package top.flya.system.domain; import lombok.Data; /** * 当前在线会话 * * @author Lion Li */ @Data 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; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysUserPost.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * 用户和岗位关联 sys_user_post * * @author Lion Li */ @Data @TableName("sys_user_post") public class SysUserPost { /** * 用户ID */ @TableId(type = IdType.INPUT) private Long userId; /** * 岗位ID */ private Long postId; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/SysUserRole.java ================================================ package top.flya.system.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * 用户和角色关联 sys_user_role * * @author Lion Li */ @Data @TableName("sys_user_role") public class SysUserRole { /** * 用户ID */ @TableId(type = IdType.INPUT) private Long userId; /** * 角色ID */ private Long roleId; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/bo/SysOssBo.java ================================================ package top.flya.system.domain.bo; import top.flya.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; /** * OSS对象存储分页查询对象 sys_oss * * @author Lion Li */ @Data @EqualsAndHashCode(callSuper = true) public class SysOssBo extends BaseEntity { /** * ossId */ private Long ossId; /** * 文件名 */ private String fileName; /** * 原名 */ private String originalName; /** * 文件后缀名 */ private String fileSuffix; /** * URL地址 */ private String url; /** * 服务商 */ private String service; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/bo/SysOssConfigBo.java ================================================ package top.flya.system.domain.bo; import top.flya.common.core.domain.BaseEntity; import top.flya.common.core.validate.AddGroup; import top.flya.common.core.validate.EditGroup; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; /** * 对象存储配置业务对象 sys_oss_config * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ @Data @EqualsAndHashCode(callSuper = true) public class SysOssConfigBo extends BaseEntity { /** * 主建 */ @NotNull(message = "主建不能为空", groups = {EditGroup.class}) private Long ossConfigId; /** * 配置key */ @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "configKey长度必须介于{min}和{max} 之间") private String configKey; /** * accessKey */ @NotBlank(message = "accessKey不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "accessKey长度必须介于{min}和{max} 之间") private String accessKey; /** * 秘钥 */ @NotBlank(message = "secretKey不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "secretKey长度必须介于{min}和{max} 之间") private String secretKey; /** * 桶名称 */ @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "bucketName长度必须介于{min}和{max}之间") private String bucketName; /** * 前缀 */ private String prefix; /** * 访问站点 */ @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "endpoint长度必须介于{min}和{max}之间") private String endpoint; /** * 自定义域名 */ private String domain; /** * 是否https(Y=是,N=否) */ private String isHttps; /** * 是否默认(0=是,1=否) */ private String status; /** * 域 */ private String region; /** * 扩展字段 */ private String ext1; /** * 备注 */ private String remark; /** * 桶权限类型(0private 1public 2custom) */ @NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class}) private String accessPolicy; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/vo/MetaVo.java ================================================ package top.flya.system.domain.vo; import top.flya.common.utils.StringUtils; import lombok.Data; /** * 路由显示信息 * * @author ruoyi */ @Data public class MetaVo { /** * 设置该路由在侧边栏和面包屑中展示的名字 */ private String title; /** * 设置该路由的图标,对应路径src/assets/icons/svg */ private String icon; /** * 设置为true,则不会被 缓存 */ private boolean noCache; /** * 内链地址(http(s)://开头) */ private String link; 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; } } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/vo/RouterVo.java ================================================ package top.flya.system.domain.vo; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Data; import java.util.List; /** * 路由配置信息 * * @author Lion Li */ @Data @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 children; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/vo/SysOssConfigVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import lombok.Data; /** * 对象存储配置视图对象 sys_oss_config * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ @Data @ExcelIgnoreUnannotated public class SysOssConfigVo { private static final long serialVersionUID = 1L; /** * 主建 */ private Long ossConfigId; /** * 配置key */ private String configKey; /** * accessKey */ private String accessKey; /** * 秘钥 */ private String secretKey; /** * 桶名称 */ private String bucketName; /** * 前缀 */ private String prefix; /** * 访问站点 */ private String endpoint; /** * 自定义域名 */ private String domain; /** * 是否https(Y=是,N=否) */ private String isHttps; /** * 域 */ private String region; /** * 是否默认(0=是,1=否) */ private String status; /** * 扩展字段 */ private String ext1; /** * 备注 */ private String remark; /** * 桶权限类型(0private 1public 2custom) */ private String accessPolicy; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/vo/SysOssVo.java ================================================ package top.flya.system.domain.vo; import lombok.Data; import java.util.Date; /** * OSS对象存储视图对象 sys_oss * * @author Lion Li */ @Data public class SysOssVo { private static final long serialVersionUID = 1L; /** * 对象存储主键 */ private Long ossId; /** * 文件名 */ private String fileName; /** * 原名 */ private String originalName; /** * 文件后缀名 */ private String fileSuffix; /** * URL地址 */ private String url; /** * 创建时间 */ private Date createTime; /** * 上传人 */ private String createBy; /** * 服务商 */ private String service; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/vo/SysUserExportVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.Date; /** * 用户对象导出VO * * @author Lion Li */ @Data @NoArgsConstructor public class SysUserExportVo implements Serializable { private static final long serialVersionUID = 1L; /** * 用户ID */ @ExcelProperty(value = "用户序号") private Long userId; /** * 用户账号 */ @ExcelProperty(value = "登录名称") private String userName; /** * 用户昵称 */ @ExcelProperty(value = "用户名称") private String nickName; /** * 用户邮箱 */ @ExcelProperty(value = "用户邮箱") private String email; /** * 手机号码 */ @ExcelProperty(value = "手机号码") private String phonenumber; /** * 用户性别 */ @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_user_sex") private String sex; /** * 帐号状态(0正常 1停用) */ @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_normal_disable") private String status; /** * 最后登录IP */ @ExcelProperty(value = "最后登录IP") private String loginIp; /** * 最后登录时间 */ @ExcelProperty(value = "最后登录时间") private Date loginDate; /** * 部门名称 */ @ExcelProperty(value = "部门名称") private String deptName; /** * 负责人 */ @ExcelProperty(value = "部门负责人") private String leader; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/domain/vo/SysUserImportVo.java ================================================ package top.flya.system.domain.vo; import com.alibaba.excel.annotation.ExcelProperty; import top.flya.common.annotation.ExcelDictFormat; import top.flya.common.convert.ExcelDictConvert; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; /** * 用户对象导入VO * * @author Lion Li */ @Data @NoArgsConstructor // @Accessors(chain = true) // 导入不允许使用 会找不到set方法 public class SysUserImportVo implements Serializable { private static final long serialVersionUID = 1L; /** * 用户ID */ @ExcelProperty(value = "用户序号") private Long userId; /** * 部门ID */ @ExcelProperty(value = "部门编号") private Long deptId; /** * 用户账号 */ @ExcelProperty(value = "登录名称") private String userName; /** * 用户昵称 */ @ExcelProperty(value = "用户名称") private String nickName; /** * 用户邮箱 */ @ExcelProperty(value = "用户邮箱") private String email; /** * 手机号码 */ @ExcelProperty(value = "手机号码") private String phonenumber; /** * 用户性别 */ @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_user_sex") private String sex; /** * 帐号状态(0正常 1停用) */ @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) @ExcelDictFormat(dictType = "sys_normal_disable") private String status; } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/listener/SysUserImportListener.java ================================================ package top.flya.system.listener; import cn.dev33.satoken.secure.BCrypt; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.excel.ExcelListener; import top.flya.common.excel.ExcelResult; import top.flya.common.exception.ServiceException; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.ValidatorUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.system.domain.vo.SysUserImportVo; import top.flya.system.service.ISysConfigService; import top.flya.system.service.ISysUserService; import lombok.extern.slf4j.Slf4j; import java.util.List; /** * 系统用户自定义导入 * * @author Lion Li */ @Slf4j public class SysUserImportListener extends AnalysisEventListener implements ExcelListener { private final ISysUserService userService; private final String password; private final Boolean isUpdateSupport; private final String operName; private int successNum = 0; private int failureNum = 0; private final StringBuilder successMsg = new StringBuilder(); private final StringBuilder failureMsg = new StringBuilder(); public SysUserImportListener(Boolean isUpdateSupport) { String initPassword = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.user.initPassword"); this.userService = SpringUtils.getBean(ISysUserService.class); this.password = BCrypt.hashpw(initPassword); this.isUpdateSupport = isUpdateSupport; this.operName = LoginHelper.getUsername(); } @Override public void invoke(SysUserImportVo userVo, AnalysisContext context) { SysUser user = this.userService.selectUserByUserName(userVo.getUserName()); try { // 验证是否存在这个用户 if (ObjectUtil.isNull(user)) { user = BeanUtil.toBean(userVo, SysUser.class); ValidatorUtils.validate(user); user.setPassword(password); // user.setCreateBy(operName); userService.insertUser(user); successNum++; successMsg.append("
        ").append(successNum).append("、账号 ").append(user.getUserName()).append(" 导入成功"); } else if (isUpdateSupport) { Long userId = user.getUserId(); user = BeanUtil.toBean(userVo, SysUser.class); user.setUserId(userId); ValidatorUtils.validate(user); userService.checkUserAllowed(user); userService.checkUserDataScope(user.getUserId()); // user.setUpdateBy(operName); userService.updateUser(user); successNum++; successMsg.append("
        ").append(successNum).append("、账号 ").append(user.getUserName()).append(" 更新成功"); } else { failureNum++; failureMsg.append("
        ").append(failureNum).append("、账号 ").append(user.getUserName()).append(" 已存在"); } } catch (Exception e) { failureNum++; String msg = "
        " + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; failureMsg.append(msg).append(e.getMessage()); log.error(msg, e); } } @Override public void doAfterAllAnalysed(AnalysisContext context) { } @Override public ExcelResult getExcelResult() { return new ExcelResult() { @Override public String getAnalysis() { if (failureNum > 0) { failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); throw new ServiceException(failureMsg.toString()); } else { successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); } return successMsg.toString(); } @Override public List getList() { return null; } @Override public List getErrorList() { return null; } }; } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysConfigMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysConfig; /** * 参数配置 数据层 * * @author Lion Li */ public interface SysConfigMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysDeptMapper.java ================================================ package top.flya.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import top.flya.common.annotation.DataColumn; import top.flya.common.annotation.DataPermission; import top.flya.common.core.domain.entity.SysDept; import top.flya.common.core.mapper.BaseMapperPlus; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 部门管理 数据层 * * @author Lion Li */ public interface SysDeptMapper extends BaseMapperPlus { /** * 查询部门管理数据 * * @param queryWrapper 查询条件 * @return 部门信息集合 */ @DataPermission({ @DataColumn(key = "deptName", value = "dept_id") }) List selectDeptList(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据角色ID查询部门树信息 * * @param roleId 角色ID * @param deptCheckStrictly 部门树选择项是否关联显示 * @return 选中部门列表 */ List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysDictDataMapper.java ================================================ package top.flya.system.mapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.entity.SysDictData; import top.flya.common.core.mapper.BaseMapperPlus; import java.util.List; /** * 字典表 数据层 * * @author Lion Li */ public interface SysDictDataMapper extends BaseMapperPlus { default List selectDictDataByType(String dictType) { return selectList( new LambdaQueryWrapper() .eq(SysDictData::getStatus, UserConstants.DICT_NORMAL) .eq(SysDictData::getDictType, dictType) .orderByAsc(SysDictData::getDictSort)); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysDictTypeMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.domain.entity.SysDictType; import top.flya.common.core.mapper.BaseMapperPlus; /** * 字典表 数据层 * * @author Lion Li */ public interface SysDictTypeMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysLogininforMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysLogininfor; /** * 系统访问日志情况信息 数据层 * * @author Lion Li */ public interface SysLogininforMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysMenuMapper.java ================================================ package top.flya.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.entity.SysMenu; import top.flya.common.core.mapper.BaseMapperPlus; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 菜单表 数据层 * * @author Lion Li */ public interface SysMenuMapper extends BaseMapperPlus { /** * 根据用户所有权限 * * @return 权限列表 */ List selectMenuPerms(); /** * 根据用户查询系统菜单列表 * * @param queryWrapper 查询条件 * @return 菜单列表 */ List selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据用户ID查询权限 * * @param userId 用户ID * @return 权限列表 */ List selectMenuPermsByUserId(Long userId); /** * 根据角色ID查询权限 * * @param roleId 角色ID * @return 权限列表 */ List selectMenuPermsByRoleId(Long roleId); /** * 根据用户ID查询菜单 * * @return 菜单列表 */ default List selectMenuTreeAll() { LambdaQueryWrapper lqw = new LambdaQueryWrapper() .in(SysMenu::getMenuType, UserConstants.TYPE_DIR, UserConstants.TYPE_MENU) .eq(SysMenu::getStatus, UserConstants.MENU_NORMAL) .orderByAsc(SysMenu::getParentId) .orderByAsc(SysMenu::getOrderNum); return this.selectList(lqw); } /** * 根据用户ID查询菜单 * * @param userId 用户ID * @return 菜单列表 */ List selectMenuTreeByUserId(Long userId); /** * 根据角色ID查询菜单树信息 * * @param roleId 角色ID * @param menuCheckStrictly 菜单树选择项是否关联显示 * @return 选中菜单列表 */ List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysNoticeMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysNotice; /** * 通知公告表 数据层 * * @author Lion Li */ public interface SysNoticeMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysOperLogMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysOperLog; /** * 操作日志 数据层 * * @author Lion Li */ public interface SysOperLogMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysOssConfigMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysOssConfig; import top.flya.system.domain.vo.SysOssConfigVo; /** * 对象存储配置Mapper接口 * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ public interface SysOssConfigMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysOssMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysOss; import top.flya.system.domain.vo.SysOssVo; /** * 文件上传 数据层 * * @author Lion Li */ public interface SysOssMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysPostMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysPost; import java.util.List; /** * 岗位信息 数据层 * * @author Lion Li */ public interface SysPostMapper extends BaseMapperPlus { /** * 根据用户ID获取岗位选择框列表 * * @param userId 用户ID * @return 选中岗位ID列表 */ List selectPostListByUserId(Long userId); /** * 查询用户所属岗位组 * * @param userName 用户名 * @return 结果 */ List selectPostsByUserName(String userName); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysRoleDeptMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysRoleDept; /** * 角色与部门关联表 数据层 * * @author Lion Li */ public interface SysRoleDeptMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysRoleMapper.java ================================================ package top.flya.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.annotation.DataColumn; import top.flya.common.annotation.DataPermission; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.core.mapper.BaseMapperPlus; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 角色表 数据层 * * @author Lion Li */ public interface SysRoleMapper extends BaseMapperPlus { @DataPermission({ @DataColumn(key = "deptName", value = "d.dept_id") }) Page selectPageRoleList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据条件分页查询角色数据 * * @param queryWrapper 查询条件 * @return 角色数据集合信息 */ @DataPermission({ @DataColumn(key = "deptName", value = "d.dept_id") }) List selectRoleList(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据用户ID查询角色 * * @param userId 用户ID * @return 角色列表 */ List selectRolePermissionByUserId(Long userId); /** * 根据用户ID获取角色选择框列表 * * @param userId 用户ID * @return 选中角色ID列表 */ List selectRoleListByUserId(Long userId); /** * 根据用户ID查询角色 * * @param userName 用户名 * @return 角色列表 */ List selectRolesByUserName(String userName); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysRoleMenuMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysRoleMenu; /** * 角色与菜单关联表 数据层 * * @author Lion Li */ public interface SysRoleMenuMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysUserMapper.java ================================================ package top.flya.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.annotation.DataColumn; import top.flya.common.annotation.DataPermission; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.mapper.BaseMapperPlus; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 用户表 数据层 * * @author Lion Li */ public interface SysUserMapper extends BaseMapperPlus { @DataPermission({ @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) Page selectPageUserList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据条件分页查询用户列表 * * @param queryWrapper 查询条件 * @return 用户信息集合信息 */ @DataPermission({ @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据条件分页查询已配用户角色列表 * * @param queryWrapper 查询条件 * @return 用户信息集合信息 */ @DataPermission({ @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) Page selectAllocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据条件分页查询未分配用户角色列表 * * @param queryWrapper 查询条件 * @return 用户信息集合信息 */ @DataPermission({ @DataColumn(key = "deptName", value = "d.dept_id"), @DataColumn(key = "userName", value = "u.user_id") }) Page selectUnallocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 通过用户名查询用户 * * @param userName 用户名 * @return 用户对象信息 */ SysUser selectUserByUserName(String userName); /** * 通过手机号查询用户 * * @param phonenumber 手机号 * @return 用户对象信息 */ SysUser selectUserByPhonenumber(String phonenumber); /** * 通过邮箱查询用户 * * @param email 邮箱 * @return 用户对象信息 */ SysUser selectUserByEmail(String email); /** * 通过用户ID查询用户 * * @param userId 用户ID * @return 用户对象信息 */ SysUser selectUserById(Long userId); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysUserPostMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysUserPost; /** * 用户与岗位关联表 数据层 * * @author Lion Li */ public interface SysUserPostMapper extends BaseMapperPlus { } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/mapper/SysUserRoleMapper.java ================================================ package top.flya.system.mapper; import top.flya.common.core.mapper.BaseMapperPlus; import top.flya.system.domain.SysUserRole; import java.util.List; /** * 用户与角色关联表 数据层 * * @author Lion Li */ public interface SysUserRoleMapper extends BaseMapperPlus { List selectUserIdsByRoleId(Long roleId); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/runner/SystemApplicationRunner.java ================================================ package top.flya.system.runner; import top.flya.common.config.RuoYiConfig; import top.flya.system.service.ISysConfigService; import top.flya.system.service.ISysDictTypeService; import top.flya.system.service.ISysOssConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; /** * 初始化 system 模块对应业务数据 * * @author Lion Li */ @Slf4j @RequiredArgsConstructor @Component public class SystemApplicationRunner implements ApplicationRunner { private final RuoYiConfig ruoyiConfig; private final ISysConfigService configService; private final ISysDictTypeService dictTypeService; private final ISysOssConfigService ossConfigService; @Override public void run(ApplicationArguments args) throws Exception { ossConfigService.init(); log.info("初始化OSS配置成功"); if (ruoyiConfig.isCacheLazy()) { return; } configService.loadingConfigCache(); log.info("加载参数缓存数据成功"); dictTypeService.loadingDictCache(); log.info("加载字典缓存数据成功"); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysConfigService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.SysConfig; import java.util.List; /** * 参数配置 服务层 * * @author Lion Li */ public interface ISysConfigService { TableDataInfo selectPageConfigList(SysConfig config, PageQuery pageQuery); /** * 查询参数配置信息 * * @param configId 参数配置ID * @return 参数配置信息 */ SysConfig selectConfigById(Long configId); /** * 根据键名查询参数配置信息 * * @param configKey 参数键名 * @return 参数键值 */ String selectConfigByKey(String configKey); /** * 获取验证码开关 * * @return true开启,false关闭 */ boolean selectCaptchaEnabled(); /** * 查询参数配置列表 * * @param config 参数配置信息 * @return 参数配置集合 */ List selectConfigList(SysConfig config); /** * 新增参数配置 * * @param config 参数配置信息 * @return 结果 */ String insertConfig(SysConfig config); /** * 修改参数配置 * * @param config 参数配置信息 * @return 结果 */ String updateConfig(SysConfig config); /** * 批量删除参数信息 * * @param configIds 需要删除的参数ID */ void deleteConfigByIds(Long[] configIds); /** * 加载参数缓存数据 */ void loadingConfigCache(); /** * 清空参数缓存数据 */ void clearConfigCache(); /** * 重置参数缓存数据 */ void resetConfigCache(); /** * 校验参数键名是否唯一 * * @param config 参数信息 * @return 结果 */ boolean checkConfigKeyUnique(SysConfig config); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysDataScopeService.java ================================================ package top.flya.system.service; /** * 通用 数据权限 服务 * * @author Lion Li */ public interface ISysDataScopeService { /** * 获取角色自定义权限 * * @param roleId 角色id * @return 部门id组 */ String getRoleCustom(Long roleId); /** * 获取部门及以下权限 * * @param deptId 部门id * @return 部门id组 */ String getDeptAndChild(Long deptId); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysDeptService.java ================================================ package top.flya.system.service; import cn.hutool.core.lang.tree.Tree; import top.flya.common.core.domain.entity.SysDept; import java.util.List; /** * 部门管理 服务层 * * @author Lion Li */ public interface ISysDeptService { /** * 查询部门管理数据 * * @param dept 部门信息 * @return 部门信息集合 */ List selectDeptList(SysDept dept); /** * 查询部门树结构信息 * * @param dept 部门信息 * @return 部门树信息集合 */ List> selectDeptTreeList(SysDept dept); /** * 构建前端所需要下拉树结构 * * @param depts 部门列表 * @return 下拉树结构列表 */ List> buildDeptTreeSelect(List depts); /** * 根据角色ID查询部门树信息 * * @param roleId 角色ID * @return 选中部门列表 */ List selectDeptListByRoleId(Long roleId); /** * 根据部门ID查询信息 * * @param deptId 部门ID * @return 部门信息 */ SysDept selectDeptById(Long deptId); /** * 根据ID查询所有子部门数(正常状态) * * @param deptId 部门ID * @return 子部门数 */ long selectNormalChildrenDeptById(Long deptId); /** * 是否存在部门子节点 * * @param deptId 部门ID * @return 结果 */ boolean hasChildByDeptId(Long deptId); /** * 查询部门是否存在用户 * * @param deptId 部门ID * @return 结果 true 存在 false 不存在 */ boolean checkDeptExistUser(Long deptId); /** * 校验部门名称是否唯一 * * @param dept 部门信息 * @return 结果 */ boolean checkDeptNameUnique(SysDept dept); /** * 校验部门是否有数据权限 * * @param deptId 部门id */ void checkDeptDataScope(Long deptId); /** * 新增保存部门信息 * * @param dept 部门信息 * @return 结果 */ int insertDept(SysDept dept); /** * 修改保存部门信息 * * @param dept 部门信息 * @return 结果 */ int updateDept(SysDept dept); /** * 删除部门管理信息 * * @param deptId 部门ID * @return 结果 */ int deleteDeptById(Long deptId); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysDictDataService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysDictData; import top.flya.common.core.page.TableDataInfo; import java.util.List; /** * 字典 业务层 * * @author Lion Li */ public interface ISysDictDataService { TableDataInfo selectPageDictDataList(SysDictData dictData, PageQuery pageQuery); /** * 根据条件分页查询字典数据 * * @param dictData 字典数据信息 * @return 字典数据集合信息 */ List selectDictDataList(SysDictData dictData); /** * 根据字典类型和字典键值查询字典数据信息 * * @param dictType 字典类型 * @param dictValue 字典键值 * @return 字典标签 */ String selectDictLabel(String dictType, String dictValue); /** * 根据字典数据ID查询信息 * * @param dictCode 字典数据ID * @return 字典数据 */ SysDictData selectDictDataById(Long dictCode); /** * 批量删除字典数据信息 * * @param dictCodes 需要删除的字典数据ID */ void deleteDictDataByIds(Long[] dictCodes); /** * 新增保存字典数据信息 * * @param dictData 字典数据信息 * @return 结果 */ List insertDictData(SysDictData dictData); /** * 修改保存字典数据信息 * * @param dictData 字典数据信息 * @return 结果 */ List updateDictData(SysDictData dictData); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysDictTypeService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysDictData; import top.flya.common.core.domain.entity.SysDictType; import top.flya.common.core.page.TableDataInfo; import java.util.List; /** * 字典 业务层 * * @author Lion Li */ public interface ISysDictTypeService { TableDataInfo selectPageDictTypeList(SysDictType dictType, PageQuery pageQuery); /** * 根据条件分页查询字典类型 * * @param dictType 字典类型信息 * @return 字典类型集合信息 */ List selectDictTypeList(SysDictType dictType); /** * 根据所有字典类型 * * @return 字典类型集合信息 */ List selectDictTypeAll(); /** * 根据字典类型查询字典数据 * * @param dictType 字典类型 * @return 字典数据集合信息 */ List selectDictDataByType(String dictType); /** * 根据字典类型ID查询信息 * * @param dictId 字典类型ID * @return 字典类型 */ SysDictType selectDictTypeById(Long dictId); /** * 根据字典类型查询信息 * * @param dictType 字典类型 * @return 字典类型 */ SysDictType selectDictTypeByType(String dictType); /** * 批量删除字典信息 * * @param dictIds 需要删除的字典ID */ void deleteDictTypeByIds(Long[] dictIds); /** * 加载字典缓存数据 */ void loadingDictCache(); /** * 清空字典缓存数据 */ void clearDictCache(); /** * 重置字典缓存数据 */ void resetDictCache(); /** * 新增保存字典类型信息 * * @param dictType 字典类型信息 * @return 结果 */ List insertDictType(SysDictType dictType); /** * 修改保存字典类型信息 * * @param dictType 字典类型信息 * @return 结果 */ List updateDictType(SysDictType dictType); /** * 校验字典类型称是否唯一 * * @param dictType 字典类型 * @return 结果 */ boolean checkDictTypeUnique(SysDictType dictType); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysLogininforService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.SysLogininfor; import java.util.List; /** * 系统访问日志情况信息 服务层 * * @author Lion Li */ public interface ISysLogininforService { TableDataInfo selectPageLogininforList(SysLogininfor logininfor, PageQuery pageQuery); /** * 新增系统登录日志 * * @param logininfor 访问日志对象 */ void insertLogininfor(SysLogininfor logininfor); /** * 查询系统登录日志集合 * * @param logininfor 访问日志对象 * @return 登录记录集合 */ List selectLogininforList(SysLogininfor logininfor); /** * 批量删除系统登录日志 * * @param infoIds 需要删除的登录日志ID * @return 结果 */ int deleteLogininforByIds(Long[] infoIds); /** * 清空系统登录日志 */ void cleanLogininfor(); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysMenuService.java ================================================ package top.flya.system.service; import cn.hutool.core.lang.tree.Tree; import top.flya.common.core.domain.entity.SysMenu; import top.flya.system.domain.vo.RouterVo; import java.util.List; import java.util.Set; /** * 菜单 业务层 * * @author Lion Li */ public interface ISysMenuService { /** * 根据用户查询系统菜单列表 * * @param userId 用户ID * @return 菜单列表 */ List selectMenuList(Long userId); /** * 根据用户查询系统菜单列表 * * @param menu 菜单信息 * @param userId 用户ID * @return 菜单列表 */ List selectMenuList(SysMenu menu, Long userId); /** * 根据用户ID查询权限 * * @param userId 用户ID * @return 权限列表 */ Set selectMenuPermsByUserId(Long userId); /** * 根据角色ID查询权限 * * @param roleId 角色ID * @return 权限列表 */ Set selectMenuPermsByRoleId(Long roleId); /** * 根据用户ID查询菜单树信息 * * @param userId 用户ID * @return 菜单列表 */ List selectMenuTreeByUserId(Long userId); /** * 根据角色ID查询菜单树信息 * * @param roleId 角色ID * @return 选中菜单列表 */ List selectMenuListByRoleId(Long roleId); /** * 构建前端路由所需要的菜单 * * @param menus 菜单列表 * @return 路由列表 */ List buildMenus(List menus); /** * 构建前端所需要下拉树结构 * * @param menus 菜单列表 * @return 下拉树结构列表 */ List> buildMenuTreeSelect(List menus); /** * 根据菜单ID查询信息 * * @param menuId 菜单ID * @return 菜单信息 */ SysMenu selectMenuById(Long menuId); /** * 是否存在菜单子节点 * * @param menuId 菜单ID * @return 结果 true 存在 false 不存在 */ boolean hasChildByMenuId(Long menuId); /** * 查询菜单是否存在角色 * * @param menuId 菜单ID * @return 结果 true 存在 false 不存在 */ boolean checkMenuExistRole(Long menuId); /** * 新增保存菜单信息 * * @param menu 菜单信息 * @return 结果 */ int insertMenu(SysMenu menu); /** * 修改保存菜单信息 * * @param menu 菜单信息 * @return 结果 */ int updateMenu(SysMenu menu); /** * 删除菜单管理信息 * * @param menuId 菜单ID * @return 结果 */ int deleteMenuById(Long menuId); /** * 校验菜单名称是否唯一 * * @param menu 菜单信息 * @return 结果 */ boolean checkMenuNameUnique(SysMenu menu); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysNoticeService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.SysNotice; import java.util.List; /** * 公告 服务层 * * @author Lion Li */ public interface ISysNoticeService { TableDataInfo selectPageNoticeList(SysNotice notice, PageQuery pageQuery); /** * 查询公告信息 * * @param noticeId 公告ID * @return 公告信息 */ SysNotice selectNoticeById(Long noticeId); /** * 查询公告列表 * * @param notice 公告信息 * @return 公告集合 */ List selectNoticeList(SysNotice notice); /** * 新增公告 * * @param notice 公告信息 * @return 结果 */ int insertNotice(SysNotice notice); /** * 修改公告 * * @param notice 公告信息 * @return 结果 */ int updateNotice(SysNotice notice); /** * 删除公告信息 * * @param noticeId 公告ID * @return 结果 */ int deleteNoticeById(Long noticeId); /** * 批量删除公告信息 * * @param noticeIds 需要删除的公告ID * @return 结果 */ int deleteNoticeByIds(Long[] noticeIds); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysOperLogService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.SysOperLog; import java.util.List; /** * 操作日志 服务层 * * @author Lion Li */ public interface ISysOperLogService { TableDataInfo selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery); /** * 新增操作日志 * * @param operLog 操作日志对象 */ void insertOperlog(SysOperLog operLog); /** * 查询系统操作日志集合 * * @param operLog 操作日志对象 * @return 操作日志集合 */ List selectOperLogList(SysOperLog operLog); /** * 批量删除系统操作日志 * * @param operIds 需要删除的操作日志ID * @return 结果 */ int deleteOperLogByIds(Long[] operIds); /** * 查询操作日志详细 * * @param operId 操作ID * @return 操作日志对象 */ SysOperLog selectOperLogById(Long operId); /** * 清空操作日志 */ void cleanOperLog(); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysOssConfigService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.bo.SysOssConfigBo; import top.flya.system.domain.vo.SysOssConfigVo; import java.util.Collection; /** * 对象存储配置Service接口 * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ public interface ISysOssConfigService { /** * 初始化OSS配置 */ void init(); /** * 查询单个 */ SysOssConfigVo queryById(Long ossConfigId); /** * 查询列表 */ TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery); /** * 根据新增业务对象插入对象存储配置 * * @param bo 对象存储配置新增业务对象 * @return */ Boolean insertByBo(SysOssConfigBo bo); /** * 根据编辑业务对象修改对象存储配置 * * @param bo 对象存储配置编辑业务对象 * @return */ Boolean updateByBo(SysOssConfigBo bo); /** * 校验并删除数据 * * @param ids 主键集合 * @param isValid 是否校验,true-删除前校验,false-不校验 * @return */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); /** * 启用停用状态 */ int updateOssConfigStatus(SysOssConfigBo bo); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysOssService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.bo.SysOssBo; import top.flya.system.domain.vo.SysOssVo; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collection; import java.util.List; /** * 文件上传 服务层 * * @author Lion Li */ public interface ISysOssService { TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); List listByIds(Collection ossIds); SysOssVo getById(Long ossId); SysOssVo upload(MultipartFile file); void download(Long ossId, HttpServletResponse response) throws IOException; Boolean deleteWithValidByIds(Collection ids, Boolean isValid); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysPostService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.SysPost; import java.util.List; /** * 岗位信息 服务层 * * @author Lion Li */ public interface ISysPostService { TableDataInfo selectPagePostList(SysPost post, PageQuery pageQuery); /** * 查询岗位信息集合 * * @param post 岗位信息 * @return 岗位列表 */ List selectPostList(SysPost post); /** * 查询所有岗位 * * @return 岗位列表 */ List selectPostAll(); /** * 通过岗位ID查询岗位信息 * * @param postId 岗位ID * @return 角色对象信息 */ SysPost selectPostById(Long postId); /** * 根据用户ID获取岗位选择框列表 * * @param userId 用户ID * @return 选中岗位ID列表 */ List selectPostListByUserId(Long userId); /** * 校验岗位名称 * * @param post 岗位信息 * @return 结果 */ boolean checkPostNameUnique(SysPost post); /** * 校验岗位编码 * * @param post 岗位信息 * @return 结果 */ boolean checkPostCodeUnique(SysPost post); /** * 通过岗位ID查询岗位使用数量 * * @param postId 岗位ID * @return 结果 */ long countUserPostById(Long postId); /** * 删除岗位信息 * * @param postId 岗位ID * @return 结果 */ int deletePostById(Long postId); /** * 批量删除岗位信息 * * @param postIds 需要删除的岗位ID * @return 结果 */ int deletePostByIds(Long[] postIds); /** * 新增保存岗位信息 * * @param post 岗位信息 * @return 结果 */ int insertPost(SysPost post); /** * 修改保存岗位信息 * * @param post 岗位信息 * @return 结果 */ int updatePost(SysPost post); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysRoleService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.core.page.TableDataInfo; import top.flya.system.domain.SysUserRole; import java.util.List; import java.util.Set; /** * 角色业务层 * * @author Lion Li */ public interface ISysRoleService { TableDataInfo selectPageRoleList(SysRole role, PageQuery pageQuery); /** * 根据条件分页查询角色数据 * * @param role 角色信息 * @return 角色数据集合信息 */ List selectRoleList(SysRole role); /** * 根据用户ID查询角色列表 * * @param userId 用户ID * @return 角色列表 */ List selectRolesByUserId(Long userId); /** * 根据用户ID查询角色权限 * * @param userId 用户ID * @return 权限列表 */ Set selectRolePermissionByUserId(Long userId); /** * 查询所有角色 * * @return 角色列表 */ List selectRoleAll(); /** * 根据用户ID获取角色选择框列表 * * @param userId 用户ID * @return 选中角色ID列表 */ List selectRoleListByUserId(Long userId); /** * 通过角色ID查询角色 * * @param roleId 角色ID * @return 角色对象信息 */ SysRole selectRoleById(Long roleId); /** * 校验角色名称是否唯一 * * @param role 角色信息 * @return 结果 */ boolean checkRoleNameUnique(SysRole role); /** * 校验角色权限是否唯一 * * @param role 角色信息 * @return 结果 */ boolean checkRoleKeyUnique(SysRole role); /** * 校验角色是否允许操作 * * @param role 角色信息 */ void checkRoleAllowed(SysRole role); /** * 校验角色是否有数据权限 * * @param roleId 角色id */ void checkRoleDataScope(Long roleId); /** * 通过角色ID查询角色使用数量 * * @param roleId 角色ID * @return 结果 */ long countUserRoleByRoleId(Long roleId); /** * 新增保存角色信息 * * @param role 角色信息 * @return 结果 */ int insertRole(SysRole role); /** * 修改保存角色信息 * * @param role 角色信息 * @return 结果 */ int updateRole(SysRole role); /** * 修改角色状态 * * @param role 角色信息 * @return 结果 */ int updateRoleStatus(SysRole role); /** * 修改数据权限信息 * * @param role 角色信息 * @return 结果 */ int authDataScope(SysRole role); /** * 通过角色ID删除角色 * * @param roleId 角色ID * @return 结果 */ int deleteRoleById(Long roleId); /** * 批量删除角色信息 * * @param roleIds 需要删除的角色ID * @return 结果 */ int deleteRoleByIds(Long[] roleIds); /** * 取消授权用户角色 * * @param userRole 用户和角色关联信息 * @return 结果 */ int deleteAuthUser(SysUserRole userRole); /** * 批量取消授权用户角色 * * @param roleId 角色ID * @param userIds 需要取消授权的用户数据ID * @return 结果 */ int deleteAuthUsers(Long roleId, Long[] userIds); /** * 批量选择授权用户角色 * * @param roleId 角色ID * @param userIds 需要删除的用户数据ID * @return 结果 */ int insertAuthUsers(Long roleId, Long[] userIds); void cleanOnlineUserByRole(Long roleId); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/ISysUserService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.page.TableDataInfo; import java.util.List; /** * 用户 业务层 * * @author Lion Li */ public interface ISysUserService { TableDataInfo selectPageUserList(SysUser user, PageQuery pageQuery); /** * 根据条件分页查询用户列表 * * @param user 用户信息 * @return 用户信息集合信息 */ List selectUserList(SysUser user); /** * 根据条件分页查询已分配用户角色列表 * * @param user 用户信息 * @return 用户信息集合信息 */ TableDataInfo selectAllocatedList(SysUser user, PageQuery pageQuery); /** * 根据条件分页查询未分配用户角色列表 * * @param user 用户信息 * @return 用户信息集合信息 */ TableDataInfo selectUnallocatedList(SysUser user, PageQuery pageQuery); /** * 通过用户名查询用户 * * @param userName 用户名 * @return 用户对象信息 */ SysUser selectUserByUserName(String userName); /** * 通过手机号查询用户 * * @param phonenumber 手机号 * @return 用户对象信息 */ SysUser selectUserByPhonenumber(String phonenumber); /** * 通过用户ID查询用户 * * @param userId 用户ID * @return 用户对象信息 */ SysUser selectUserById(Long userId); /** * 根据用户ID查询用户所属角色组 * * @param userName 用户名 * @return 结果 */ String selectUserRoleGroup(String userName); /** * 根据用户ID查询用户所属岗位组 * * @param userName 用户名 * @return 结果 */ String selectUserPostGroup(String userName); /** * 校验用户名称是否唯一 * * @param user 用户信息 * @return 结果 */ boolean checkUserNameUnique(SysUser user); /** * 校验手机号码是否唯一 * * @param user 用户信息 * @return 结果 */ boolean checkPhoneUnique(SysUser user); /** * 校验email是否唯一 * * @param user 用户信息 * @return 结果 */ boolean checkEmailUnique(SysUser user); /** * 校验用户是否允许操作 * * @param user 用户信息 */ void checkUserAllowed(SysUser user); /** * 校验用户是否有数据权限 * * @param userId 用户id */ void checkUserDataScope(Long userId); /** * 新增用户信息 * * @param user 用户信息 * @return 结果 */ int insertUser(SysUser user); /** * 注册用户信息 * * @param user 用户信息 * @return 结果 */ boolean registerUser(SysUser user); /** * 修改用户信息 * * @param user 用户信息 * @return 结果 */ int updateUser(SysUser user); /** * 用户授权角色 * * @param userId 用户ID * @param roleIds 角色组 */ void insertUserAuth(Long userId, Long[] roleIds); /** * 修改用户状态 * * @param user 用户信息 * @return 结果 */ int updateUserStatus(SysUser user); /** * 修改用户基本信息 * * @param user 用户信息 * @return 结果 */ int updateUserProfile(SysUser user); /** * 修改用户头像 * * @param userName 用户名 * @param avatar 头像地址 * @return 结果 */ boolean updateUserAvatar(String userName, String avatar); /** * 重置用户密码 * * @param user 用户信息 * @return 结果 */ int resetPwd(SysUser user); /** * 重置用户密码 * * @param userName 用户名 * @param password 密码 * @return 结果 */ int resetUserPwd(String userName, String password); /** * 通过用户ID删除用户 * * @param userId 用户ID * @return 结果 */ int deleteUserById(Long userId); /** * 批量删除用户信息 * * @param userIds 需要删除的用户ID * @return 结果 */ int deleteUserByIds(Long[] userIds); } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/SysLoginService.java ================================================ package top.flya.system.service; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.secure.BCrypt; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import top.flya.common.constant.CacheConstants; import top.flya.common.constant.Constants; import top.flya.common.core.domain.R; import top.flya.common.core.domain.event.LogininforEvent; import top.flya.common.core.domain.dto.RoleDTO; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.core.domain.model.XcxLoginUser; import top.flya.common.enums.DeviceType; import top.flya.common.enums.LoginType; import top.flya.common.enums.UserStatus; import top.flya.common.exception.user.CaptchaException; import top.flya.common.exception.user.CaptchaExpireException; import top.flya.common.exception.user.UserException; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.DateUtils; import top.flya.common.utils.MessageUtils; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.RedisUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.system.mapper.SysUserMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.time.Duration; import java.util.List; import java.util.function.Supplier; /** * 登录校验方法 * * @author Lion Li */ @RequiredArgsConstructor @Slf4j @Service public class SysLoginService { private final SysUserMapper userMapper; private final ISysConfigService configService; private final SysPermissionService permissionService; @Value("${user.password.maxRetryCount}") private Integer maxRetryCount; @Value("${user.password.lockTime}") private Integer lockTime; /** * 登录验证 * * @param username 用户名 * @param password 密码 * @param code 验证码 * @param uuid 唯一标识 * @return 结果 */ public String login(String username, String password, String code, String uuid) { boolean captchaEnabled = configService.selectCaptchaEnabled(); // 验证码开关 if (captchaEnabled) { validateCaptcha(username, code, uuid); } SysUser user = loadUserByUsername(username); checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword())); // 此处可根据登录用户的数据不同 自行创建 loginUser LoginUser loginUser = buildLoginUser(user); // 生成token LoginHelper.loginByDevice(loginUser, DeviceType.PC); recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); recordLoginInfo(user.getUserId(), username); return StpUtil.getTokenValue(); } public String smsLogin(String phonenumber, String smsCode) { // 通过手机号查找用户 SysUser user = loadUserByPhonenumber(phonenumber); checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode)); // 此处可根据登录用户的数据不同 自行创建 loginUser LoginUser loginUser = buildLoginUser(user); // 生成token LoginHelper.loginByDevice(loginUser, DeviceType.APP); recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); recordLoginInfo(user.getUserId(), user.getUserName()); return StpUtil.getTokenValue(); } public String emailLogin(String email, String emailCode) { // 通过手机号查找用户 SysUser user = loadUserByEmail(email); checkLogin(LoginType.EMAIL, user.getUserName(), () -> !validateEmailCode(email, emailCode)); // 此处可根据登录用户的数据不同 自行创建 loginUser LoginUser loginUser = buildLoginUser(user); // 生成token LoginHelper.loginByDevice(loginUser, DeviceType.APP); recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); recordLoginInfo(user.getUserId(), user.getUserName()); return StpUtil.getTokenValue(); } public String xcxLogin(String xcxCode) { // String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + // "&secret=" + secret + "&js_code=" + xcxCode + "&grant_type=authorization_code"; // log.info("微信小程序登录 url : {}", url); // String response = HttpUtil.get(url); // // JSONObject wxUser = JSONObject.parseObject(response); // if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(wxUser) || wxUser.get("errcode") != null) { // return null; // } // String openId = wxUser.get("openid").toString(); // //如果存在 就直接返回 不存在就新建用户 // PzcUser user = userMapper.selectOne(new QueryWrapper().eq("openid", openId)); // SysUser user = loadUserByOpenid(openid); // // // 此处可根据登录用户的数据不同 自行创建 loginUser // XcxLoginUser loginUser = new XcxLoginUser(); // loginUser.setUserId(user.getUserId()); // loginUser.setUsername(user.getUserName()); // loginUser.setUserType(user.getUserType()); // loginUser.setOpenid(openId); // // 生成token // LoginHelper.loginByDevice(loginUser, DeviceType.XCX); // // recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); // recordLoginInfo(user.getUserId(), user.getUserName()); // return StpUtil.getTokenValue(); return null; } /** * 退出登录 */ public void logout() { try { LoginUser loginUser = LoginHelper.getLoginUser(); StpUtil.logout(); recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); } catch (NotLoginException ignored) { } } /** * 记录登录信息 * * @param username 用户名 * @param status 状态 * @param message 消息内容 */ private void recordLogininfor(String username, String status, String message) { LogininforEvent logininforEvent = new LogininforEvent(); logininforEvent.setUsername(username); logininforEvent.setStatus(status); logininforEvent.setMessage(message); logininforEvent.setRequest(ServletUtils.getRequest()); SpringUtils.context().publishEvent(logininforEvent); } /** * 校验短信验证码 */ private boolean validateSmsCode(String phonenumber, String smsCode) { String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + phonenumber); if (StringUtils.isBlank(code)) { recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); throw new CaptchaExpireException(); } return code.equals(smsCode); } /** * 校验邮箱验证码 */ private boolean validateEmailCode(String email, String emailCode) { String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + email); if (StringUtils.isBlank(code)) { recordLogininfor(email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); throw new CaptchaExpireException(); } return code.equals(emailCode); } /** * 校验验证码 * * @param username 用户名 * @param code 验证码 * @param uuid 唯一标识 */ public void validateCaptcha(String username, String code, String uuid) { String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); if (captcha == null) { recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); throw new CaptchaExpireException(); } if (!code.equalsIgnoreCase(captcha)) { recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); throw new CaptchaException(); } } private SysUser loadUserByUsername(String username) { SysUser user = userMapper.selectOne(new LambdaQueryWrapper() .select(SysUser::getUserName, SysUser::getStatus) .eq(SysUser::getUserName, username)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new UserException("user.not.exists", username); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new UserException("user.blocked", username); } return userMapper.selectUserByUserName(username); } private SysUser loadUserByPhonenumber(String phonenumber) { SysUser user = userMapper.selectOne(new LambdaQueryWrapper() .select(SysUser::getPhonenumber, SysUser::getStatus) .eq(SysUser::getPhonenumber, phonenumber)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", phonenumber); throw new UserException("user.not.exists", phonenumber); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", phonenumber); throw new UserException("user.blocked", phonenumber); } return userMapper.selectUserByPhonenumber(phonenumber); } private SysUser loadUserByEmail(String email) { SysUser user = userMapper.selectOne(new LambdaQueryWrapper() .select(SysUser::getPhonenumber, SysUser::getStatus) .eq(SysUser::getEmail, email)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", email); throw new UserException("user.not.exists", email); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", email); throw new UserException("user.blocked", email); } return userMapper.selectUserByEmail(email); } private SysUser loadUserByOpenid(String openid) { // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 // todo 自行实现 userService.selectUserByOpenid(openid); SysUser user = new SysUser(); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", openid); // todo 用户不存在 业务逻辑自行实现 } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", openid); // todo 用户已被停用 业务逻辑自行实现 } return user; } /** * 构建登录用户 */ private LoginUser buildLoginUser(SysUser user) { LoginUser loginUser = new LoginUser(); loginUser.setUserId(user.getUserId()); loginUser.setDeptId(user.getDeptId()); loginUser.setUsername(user.getUserName()); loginUser.setUserType(user.getUserType()); loginUser.setMenuPermission(permissionService.getMenuPermission(user)); loginUser.setRolePermission(permissionService.getRolePermission(user)); loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName()); List roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class); loginUser.setRoles(roles); return loginUser; } /** * 记录登录信息 * * @param userId 用户ID */ public void recordLoginInfo(Long userId, String username) { SysUser sysUser = new SysUser(); sysUser.setUserId(userId); sysUser.setLoginIp(ServletUtils.getClientIP()); sysUser.setLoginDate(DateUtils.getNowDate()); // sysUser.setUpdateBy(username); userMapper.updateById(sysUser); } /** * 登录校验 */ private void checkLogin(LoginType loginType, String username, Supplier supplier) { String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; String loginFail = Constants.LOGIN_FAIL; // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip) Integer errorNumber = RedisUtils.getCacheObject(errorKey); // 锁定时间内登录 则踢出 if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) { recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); } if (supplier.get()) { // 是否第一次 errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1; // 达到规定错误次数 则锁定登录 if (errorNumber.equals(maxRetryCount)) { RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); } else { // 未达到规定错误次数 则递增 RedisUtils.setCacheObject(errorKey, errorNumber); recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); throw new UserException(loginType.getRetryLimitCount(), errorNumber); } } // 登录成功 清空错误次数 RedisUtils.deleteObject(errorKey); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/SysPermissionService.java ================================================ package top.flya.system.service; import top.flya.common.core.domain.entity.SysUser; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.Set; /** * 用户权限处理 * * @author ruoyi */ @RequiredArgsConstructor @Service public class SysPermissionService { private final ISysRoleService roleService; private final ISysMenuService menuService; /** * 获取角色数据权限 * * @param user 用户信息 * @return 角色权限信息 */ public Set getRolePermission(SysUser user) { Set roles = new HashSet<>(); // 管理员拥有所有权限 if (user.isAdmin()) { roles.add("admin"); } else { roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); } return roles; } /** * 获取菜单数据权限 * * @param user 用户信息 * @return 菜单权限信息 */ public Set getMenuPermission(SysUser user) { Set perms = new HashSet<>(); // 管理员拥有所有权限 if (user.isAdmin()) { perms.add("*:*:*"); } else { perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); } return perms; } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/SysRegisterService.java ================================================ package top.flya.system.service; import cn.dev33.satoken.secure.BCrypt; import top.flya.common.constant.CacheConstants; import top.flya.common.constant.Constants; import top.flya.common.core.domain.event.LogininforEvent; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.domain.model.RegisterBody; import top.flya.common.enums.UserType; import top.flya.common.exception.user.CaptchaException; import top.flya.common.exception.user.CaptchaExpireException; import top.flya.common.exception.user.UserException; import top.flya.common.utils.MessageUtils; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.RedisUtils; import top.flya.common.utils.spring.SpringUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; /** * 注册校验方法 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysRegisterService { private final ISysUserService userService; private final ISysConfigService configService; /** * 注册 */ public void register(RegisterBody registerBody) { String username = registerBody.getUsername(); String password = registerBody.getPassword(); // 校验用户类型是否存在 String userType = UserType.getUserType(registerBody.getUserType()).getUserType(); boolean captchaEnabled = configService.selectCaptchaEnabled(); // 验证码开关 if (captchaEnabled) { validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); } SysUser sysUser = new SysUser(); sysUser.setUserName(username); sysUser.setNickName(username); sysUser.setPassword(BCrypt.hashpw(password)); sysUser.setUserType(userType); if (!userService.checkUserNameUnique(sysUser)) { throw new UserException("user.register.save.error", username); } boolean regFlag = userService.registerUser(sysUser); if (!regFlag) { throw new UserException("user.register.error"); } recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success")); } /** * 校验验证码 * * @param username 用户名 * @param code 验证码 * @param uuid 唯一标识 */ public void validateCaptcha(String username, String code, String uuid) { String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); if (captcha == null) { recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire")); throw new CaptchaExpireException(); } if (!code.equalsIgnoreCase(captcha)) { recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error")); throw new CaptchaException(); } } /** * 记录登录信息 * * @param username 用户名 * @param status 状态 * @param message 消息内容 * @return */ private void recordLogininfor(String username, String status, String message) { LogininforEvent logininforEvent = new LogininforEvent(); logininforEvent.setUsername(username); logininforEvent.setStatus(status); logininforEvent.setMessage(message); logininforEvent.setRequest(ServletUtils.getRequest()); SpringUtils.context().publishEvent(logininforEvent); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysConfigServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.constant.CacheNames; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.service.ConfigService; import top.flya.common.exception.ServiceException; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.CacheUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.system.domain.SysConfig; import top.flya.system.mapper.SysConfigMapper; import top.flya.system.service.ISysConfigService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; import java.util.Map; /** * 参数配置 服务层实现 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysConfigServiceImpl implements ISysConfigService, ConfigService { private final SysConfigMapper baseMapper; @Override public TableDataInfo selectPageConfigList(SysConfig config, PageQuery pageQuery) { Map params = config.getParams(); LambdaQueryWrapper lqw = new LambdaQueryWrapper() .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName()) .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType()) .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysConfig::getCreateTime, params.get("beginTime"), params.get("endTime")); Page page = baseMapper.selectPage(pageQuery.build(), lqw); return TableDataInfo.build(page); } /** * 查询参数配置信息 * * @param configId 参数配置ID * @return 参数配置信息 */ @Override @DS("master") public SysConfig selectConfigById(Long configId) { return baseMapper.selectById(configId); } /** * 根据键名查询参数配置信息 * * @param configKey 参数key * @return 参数键值 */ @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey") @Override public String selectConfigByKey(String configKey) { SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper() .eq(SysConfig::getConfigKey, configKey)); if (ObjectUtil.isNotNull(retConfig)) { return retConfig.getConfigValue(); } return StringUtils.EMPTY; } /** * 获取验证码开关 * * @return true开启,false关闭 */ @Override public boolean selectCaptchaEnabled() { String captchaEnabled = SpringUtils.getAopProxy(this).selectConfigByKey("sys.account.captchaEnabled"); if (StringUtils.isEmpty(captchaEnabled)) { return true; } return Convert.toBool(captchaEnabled); } /** * 查询参数配置列表 * * @param config 参数配置信息 * @return 参数配置集合 */ @Override public List selectConfigList(SysConfig config) { Map params = config.getParams(); LambdaQueryWrapper lqw = new LambdaQueryWrapper() .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName()) .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType()) .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysConfig::getCreateTime, params.get("beginTime"), params.get("endTime")); return baseMapper.selectList(lqw); } /** * 新增参数配置 * * @param config 参数配置信息 * @return 结果 */ @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#config.configKey") @Override public String insertConfig(SysConfig config) { int row = baseMapper.insert(config); if (row > 0) { return config.getConfigValue(); } throw new ServiceException("操作失败"); } /** * 修改参数配置 * * @param config 参数配置信息 * @return 结果 */ @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#config.configKey") @Override public String updateConfig(SysConfig config) { int row = 0; if (config.getConfigId() != null) { SysConfig temp = baseMapper.selectById(config.getConfigId()); if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) { CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey()); } row = baseMapper.updateById(config); } else { row = baseMapper.update(config, new LambdaQueryWrapper() .eq(SysConfig::getConfigKey, config.getConfigKey())); } if (row > 0) { return config.getConfigValue(); } throw new ServiceException("操作失败"); } /** * 批量删除参数信息 * * @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())); } CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); } baseMapper.deleteBatchIds(Arrays.asList(configIds)); } /** * 加载参数缓存数据 */ @Override public void loadingConfigCache() { List configsList = selectConfigList(new SysConfig()); configsList.forEach(config -> CacheUtils.put(CacheNames.SYS_CONFIG, config.getConfigKey(), config.getConfigValue())); } /** * 清空参数缓存数据 */ @Override public void clearConfigCache() { CacheUtils.clear(CacheNames.SYS_CONFIG); } /** * 重置参数缓存数据 */ @Override public void resetConfigCache() { clearConfigCache(); loadingConfigCache(); } /** * 校验参数键名是否唯一 * * @param config 参数配置信息 * @return 结果 */ @Override public boolean checkConfigKeyUnique(SysConfig config) { long configId = ObjectUtil.isNull(config.getConfigId()) ? -1L : config.getConfigId(); SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper().eq(SysConfig::getConfigKey, config.getConfigKey())); if (ObjectUtil.isNotNull(info) && info.getConfigId() != configId) { return false; } return true; } /** * 根据参数 key 获取参数值 * * @param configKey 参数 key * @return 参数值 */ @Override public String getConfigValue(String configKey) { return SpringUtils.getAopProxy(this).selectConfigByKey(configKey); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysDataScopeServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import top.flya.common.core.domain.entity.SysDept; import top.flya.common.helper.DataBaseHelper; import top.flya.common.utils.StreamUtils; import top.flya.system.domain.SysRoleDept; import top.flya.system.mapper.SysDeptMapper; import top.flya.system.mapper.SysRoleDeptMapper; import top.flya.system.service.ISysDataScopeService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; /** * 数据权限 实现 *

        * 注意: 此Service内不允许调用标注`数据权限`注解的方法 * 例如: deptMapper.selectList 此 selectList 方法标注了`数据权限`注解 会出现循环解析的问题 * * @author Lion Li */ @RequiredArgsConstructor @Service("sdss") public class SysDataScopeServiceImpl implements ISysDataScopeService { private final SysRoleDeptMapper roleDeptMapper; private final SysDeptMapper deptMapper; @Override public String getRoleCustom(Long roleId) { List list = roleDeptMapper.selectList( new LambdaQueryWrapper() .select(SysRoleDept::getDeptId) .eq(SysRoleDept::getRoleId, roleId)); if (CollUtil.isNotEmpty(list)) { return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId())); } return null; } @Override public String getDeptAndChild(Long deptId) { List deptList = deptMapper.selectList(new LambdaQueryWrapper() .select(SysDept::getDeptId) .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); List ids = StreamUtils.toList(deptList, SysDept::getDeptId); ids.add(deptId); List list = deptMapper.selectList(new LambdaQueryWrapper() .select(SysDept::getDeptId) .in(SysDept::getDeptId, ids)); if (CollUtil.isNotEmpty(list)) { return StreamUtils.join(list, d -> Convert.toStr(d.getDeptId())); } return null; } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysDeptServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import top.flya.common.constant.CacheNames; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.entity.SysDept; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.service.DeptService; import top.flya.common.exception.ServiceException; import top.flya.common.helper.DataBaseHelper; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StringUtils; import top.flya.common.utils.TreeBuildUtils; import top.flya.common.utils.redis.CacheUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.system.mapper.SysDeptMapper; import top.flya.system.mapper.SysRoleMapper; import top.flya.system.mapper.SysUserMapper; import top.flya.system.service.ISysDeptService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 部门管理 服务实现 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysDeptServiceImpl implements ISysDeptService, DeptService { private final SysDeptMapper baseMapper; private final SysRoleMapper roleMapper; private final SysUserMapper userMapper; /** * 查询部门管理数据 * * @param dept 部门信息 * @return 部门信息集合 */ @Override public List selectDeptList(SysDept dept) { LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); lqw.eq(SysDept::getDelFlag, "0") .eq(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId()) .eq(ObjectUtil.isNotNull(dept.getParentId()), SysDept::getParentId, dept.getParentId()) .like(StringUtils.isNotBlank(dept.getDeptName()), SysDept::getDeptName, dept.getDeptName()) .eq(StringUtils.isNotBlank(dept.getStatus()), SysDept::getStatus, dept.getStatus()) .orderByAsc(SysDept::getParentId) .orderByAsc(SysDept::getOrderNum); return baseMapper.selectDeptList(lqw); } /** * 查询部门树结构信息 * * @param dept 部门信息 * @return 部门树信息集合 */ @Override public List> selectDeptTreeList(SysDept dept) { List depts = this.selectDeptList(dept); return buildDeptTreeSelect(depts); } /** * 构建前端所需要下拉树结构 * * @param depts 部门列表 * @return 下拉树结构列表 */ @Override public List> buildDeptTreeSelect(List depts) { if (CollUtil.isEmpty(depts)) { return CollUtil.newArrayList(); } return TreeBuildUtils.build(depts, (dept, tree) -> tree.setId(dept.getDeptId()) .setParentId(dept.getParentId()) .setName(dept.getDeptName()) .setWeight(dept.getOrderNum())); } /** * 根据角色ID查询部门树信息 * * @param roleId 角色ID * @return 选中部门列表 */ @Override public List selectDeptListByRoleId(Long roleId) { SysRole role = roleMapper.selectById(roleId); return baseMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly()); } /** * 根据部门ID查询信息 * * @param deptId 部门ID * @return 部门信息 */ @Cacheable(cacheNames = CacheNames.SYS_DEPT, key = "#deptId") @Override public SysDept selectDeptById(Long deptId) { SysDept dept = baseMapper.selectById(deptId); if (ObjectUtil.isNull(dept)) { return null; } SysDept parentDept = baseMapper.selectOne(new LambdaQueryWrapper() .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId())); dept.setParentName(ObjectUtil.isNotNull(parentDept) ? parentDept.getDeptName() : null); return dept; } /** * 通过部门ID查询部门名称 * * @param deptIds 部门ID串逗号分隔 * @return 部门名称串逗号分隔 */ @Override public String selectDeptNameByIds(String deptIds) { List list = new ArrayList<>(); for (Long id : StringUtils.splitTo(deptIds, Convert::toLong)) { SysDept dept = SpringUtils.getAopProxy(this).selectDeptById(id); if (ObjectUtil.isNotNull(dept)) { list.add(dept.getDeptName()); } } return String.join(StringUtils.SEPARATOR, list); } /** * 根据ID查询所有子部门数(正常状态) * * @param deptId 部门ID * @return 子部门数 */ @Override public long selectNormalChildrenDeptById(Long deptId) { return baseMapper.selectCount(new LambdaQueryWrapper() .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL) .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); } /** * 是否存在子节点 * * @param deptId 部门ID * @return 结果 */ @Override public boolean hasChildByDeptId(Long deptId) { return baseMapper.exists(new LambdaQueryWrapper() .eq(SysDept::getParentId, deptId)); } /** * 查询部门是否存在用户 * * @param deptId 部门ID * @return 结果 true 存在 false 不存在 */ @Override public boolean checkDeptExistUser(Long deptId) { return userMapper.exists(new LambdaQueryWrapper() .eq(SysUser::getDeptId, deptId)); } /** * 校验部门名称是否唯一 * * @param dept 部门信息 * @return 结果 */ @Override public boolean checkDeptNameUnique(SysDept dept) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysDept::getDeptName, dept.getDeptName()) .eq(SysDept::getParentId, dept.getParentId()) .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())); return !exist; } /** * 校验部门是否有数据权限 * * @param deptId 部门id */ @Override public void checkDeptDataScope(Long deptId) { if (!LoginHelper.isAdmin()) { SysDept dept = new SysDept(); dept.setDeptId(deptId); List depts = this.selectDeptList(dept); if (CollUtil.isEmpty(depts)) { throw new ServiceException("没有权限访问部门数据!"); } } } /** * 新增保存部门信息 * * @param dept 部门信息 * @return 结果 */ @Override public int insertDept(SysDept dept) { SysDept info = baseMapper.selectById(dept.getParentId()); // 如果父节点不为正常状态,则不允许新增子节点 if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) { throw new ServiceException("部门停用,不允许新增"); } dept.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + dept.getParentId()); return baseMapper.insert(dept); } /** * 修改保存部门信息 * * @param dept 部门信息 * @return 结果 */ @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#dept.deptId") @Override public int updateDept(SysDept dept) { SysDept newParentDept = baseMapper.selectById(dept.getParentId()); SysDept oldDept = baseMapper.selectById(dept.getDeptId()); if (ObjectUtil.isNotNull(newParentDept) && ObjectUtil.isNotNull(oldDept)) { String newAncestors = newParentDept.getAncestors() + StringUtils.SEPARATOR + newParentDept.getDeptId(); String oldAncestors = oldDept.getAncestors(); dept.setAncestors(newAncestors); updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); } int result = baseMapper.updateById(dept); if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) && !StringUtils.equals(UserConstants.DEPT_NORMAL, dept.getAncestors())) { // 如果该部门是启用状态,则启用该部门的所有上级部门 updateParentDeptStatusNormal(dept); } return result; } /** * 修改该部门的父级部门状态 * * @param dept 当前部门 */ private void updateParentDeptStatusNormal(SysDept dept) { String ancestors = dept.getAncestors(); Long[] deptIds = Convert.toLongArray(ancestors); baseMapper.update(null, new LambdaUpdateWrapper() .set(SysDept::getStatus, UserConstants.DEPT_NORMAL) .in(SysDept::getDeptId, Arrays.asList(deptIds))); } /** * 修改子元素关系 * * @param deptId 被修改的部门ID * @param newAncestors 新的父ID集合 * @param oldAncestors 旧的父ID集合 */ public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) { List children = baseMapper.selectList(new LambdaQueryWrapper() .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); List list = new ArrayList<>(); for (SysDept child : children) { SysDept dept = new SysDept(); dept.setDeptId(child.getDeptId()); dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); list.add(dept); } if (CollUtil.isNotEmpty(list)) { if (baseMapper.updateBatchById(list)) { list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId())); } } } /** * 删除部门管理信息 * * @param deptId 部门ID * @return 结果 */ @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#deptId") @Override public int deleteDeptById(Long deptId) { return baseMapper.deleteById(deptId); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysDictDataServiceImpl.java ================================================ package top.flya.system.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.constant.CacheNames; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysDictData; import top.flya.common.core.page.TableDataInfo; import top.flya.common.exception.ServiceException; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.CacheUtils; import top.flya.system.mapper.SysDictDataMapper; import top.flya.system.service.ISysDictDataService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CachePut; import org.springframework.stereotype.Service; import java.util.List; /** * 字典 业务层处理 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysDictDataServiceImpl implements ISysDictDataService { private final SysDictDataMapper baseMapper; @Override public TableDataInfo selectPageDictDataList(SysDictData dictData, PageQuery pageQuery) { LambdaQueryWrapper lqw = new LambdaQueryWrapper() .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType()) .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel()) .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus()) .orderByAsc(SysDictData::getDictSort); Page page = baseMapper.selectPage(pageQuery.build(), lqw); return TableDataInfo.build(page); } /** * 根据条件分页查询字典数据 * * @param dictData 字典数据信息 * @return 字典数据集合信息 */ @Override public List selectDictDataList(SysDictData dictData) { return baseMapper.selectList(new LambdaQueryWrapper() .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType()) .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel()) .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus()) .orderByAsc(SysDictData::getDictSort)); } /** * 根据字典类型和字典键值查询字典数据信息 * * @param dictType 字典类型 * @param dictValue 字典键值 * @return 字典标签 */ @Override public String selectDictLabel(String dictType, String dictValue) { return baseMapper.selectOne(new LambdaQueryWrapper() .select(SysDictData::getDictLabel) .eq(SysDictData::getDictType, dictType) .eq(SysDictData::getDictValue, dictValue)) .getDictLabel(); } /** * 根据字典数据ID查询信息 * * @param dictCode 字典数据ID * @return 字典数据 */ @Override public SysDictData selectDictDataById(Long dictCode) { return baseMapper.selectById(dictCode); } /** * 批量删除字典数据信息 * * @param dictCodes 需要删除的字典数据ID */ @Override public void deleteDictDataByIds(Long[] dictCodes) { for (Long dictCode : dictCodes) { SysDictData data = selectDictDataById(dictCode); baseMapper.deleteById(dictCode); CacheUtils.evict(CacheNames.SYS_DICT, data.getDictType()); } } /** * 新增保存字典数据信息 * * @param data 字典数据信息 * @return 结果 */ @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#data.dictType") @Override public List insertDictData(SysDictData data) { int row = baseMapper.insert(data); if (row > 0) { return baseMapper.selectDictDataByType(data.getDictType()); } throw new ServiceException("操作失败"); } /** * 修改保存字典数据信息 * * @param data 字典数据信息 * @return 结果 */ @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#data.dictType") @Override public List updateDictData(SysDictData data) { int row = baseMapper.updateById(data); if (row > 0) { return baseMapper.selectDictDataByType(data.getDictType()); } throw new ServiceException("操作失败"); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysDictTypeServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.dev33.satoken.context.SaHolder; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.constant.CacheConstants; import top.flya.common.constant.CacheNames; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysDictData; import top.flya.common.core.domain.entity.SysDictType; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.service.DictService; import top.flya.common.exception.ServiceException; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.CacheUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.system.mapper.SysDictDataMapper; import top.flya.system.mapper.SysDictTypeMapper; import top.flya.system.service.ISysDictTypeService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.stream.Collectors; /** * 字典 业务层处理 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService { private final SysDictTypeMapper baseMapper; private final SysDictDataMapper dictDataMapper; @Override public TableDataInfo selectPageDictTypeList(SysDictType dictType, PageQuery pageQuery) { Map params = dictType.getParams(); LambdaQueryWrapper lqw = new LambdaQueryWrapper() .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName()) .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus()) .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysDictType::getCreateTime, params.get("beginTime"), params.get("endTime")); Page page = baseMapper.selectPage(pageQuery.build(), lqw); return TableDataInfo.build(page); } /** * 根据条件分页查询字典类型 * * @param dictType 字典类型信息 * @return 字典类型集合信息 */ @Override public List selectDictTypeList(SysDictType dictType) { Map params = dictType.getParams(); return baseMapper.selectList(new LambdaQueryWrapper() .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName()) .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus()) .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysDictType::getCreateTime, params.get("beginTime"), params.get("endTime"))); } /** * 根据所有字典类型 * * @return 字典类型集合信息 */ @Override public List selectDictTypeAll() { return baseMapper.selectList(); } /** * 根据字典类型查询字典数据 * * @param dictType 字典类型 * @return 字典数据集合信息 */ @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType") @Override public List selectDictDataByType(String dictType) { List dictDatas = dictDataMapper.selectDictDataByType(dictType); if (CollUtil.isNotEmpty(dictDatas)) { return dictDatas; } return null; } /** * 根据字典类型ID查询信息 * * @param dictId 字典类型ID * @return 字典类型 */ @Override public SysDictType selectDictTypeById(Long dictId) { return baseMapper.selectById(dictId); } /** * 根据字典类型查询信息 * * @param dictType 字典类型 * @return 字典类型 */ @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType") @Override public SysDictType selectDictTypeByType(String dictType) { return baseMapper.selectById(new LambdaQueryWrapper().eq(SysDictType::getDictType, dictType)); } /** * 批量删除字典类型信息 * * @param dictIds 需要删除的字典ID */ @Override public void deleteDictTypeByIds(Long[] dictIds) { for (Long dictId : dictIds) { SysDictType dictType = selectDictTypeById(dictId); if (dictDataMapper.exists(new LambdaQueryWrapper() .eq(SysDictData::getDictType, dictType.getDictType()))) { throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); } CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType()); } baseMapper.deleteBatchIds(Arrays.asList(dictIds)); } /** * 加载字典缓存数据 */ @Override public void loadingDictCache() { List dictDataList = dictDataMapper.selectList( new LambdaQueryWrapper().eq(SysDictData::getStatus, UserConstants.DICT_NORMAL)); Map> dictDataMap = StreamUtils.groupByKey(dictDataList, SysDictData::getDictType); dictDataMap.forEach((k,v) -> { List dictList = StreamUtils.sorted(v, Comparator.comparing(SysDictData::getDictSort)); CacheUtils.put(CacheNames.SYS_DICT, k, dictList); }); } /** * 清空字典缓存数据 */ @Override public void clearDictCache() { CacheUtils.clear(CacheNames.SYS_DICT); } /** * 重置字典缓存数据 */ @Override public void resetDictCache() { clearDictCache(); loadingDictCache(); } /** * 新增保存字典类型信息 * * @param dict 字典类型信息 * @return 结果 */ @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#dict.dictType") @Override public List insertDictType(SysDictType dict) { int row = baseMapper.insert(dict); if (row > 0) { return new ArrayList<>(); } throw new ServiceException("操作失败"); } /** * 修改保存字典类型信息 * * @param dict 字典类型信息 * @return 结果 */ @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#dict.dictType") @Override @Transactional(rollbackFor = Exception.class) public List updateDictType(SysDictType dict) { SysDictType oldDict = baseMapper.selectById(dict.getDictId()); dictDataMapper.update(null, new LambdaUpdateWrapper() .set(SysDictData::getDictType, dict.getDictType()) .eq(SysDictData::getDictType, oldDict.getDictType())); int row = baseMapper.updateById(dict); if (row > 0) { CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType()); return dictDataMapper.selectDictDataByType(dict.getDictType()); } throw new ServiceException("操作失败"); } /** * 校验字典类型称是否唯一 * * @param dict 字典类型 * @return 结果 */ @Override public boolean checkDictTypeUnique(SysDictType dict) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysDictType::getDictType, dict.getDictType()) .ne(ObjectUtil.isNotNull(dict.getDictId()), SysDictType::getDictId, dict.getDictId())); return !exist; } /** * 根据字典类型和字典值获取字典标签 * * @param dictType 字典类型 * @param dictValue 字典值 * @param separator 分隔符 * @return 字典标签 */ @SuppressWarnings("unchecked cast") @Override public String getDictLabel(String dictType, String dictValue, String separator) { // 优先从本地缓存获取 List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); if (ObjectUtil.isNull(datas)) { datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); } Map map = StreamUtils.toMap(datas, SysDictData::getDictValue, SysDictData::getDictLabel); if (StringUtils.containsAny(dictValue, separator)) { return Arrays.stream(dictValue.split(separator)) .map(v -> map.getOrDefault(v, StringUtils.EMPTY)) .collect(Collectors.joining(separator)); } else { return map.getOrDefault(dictValue, StringUtils.EMPTY); } } /** * 根据字典类型和字典标签获取字典值 * * @param dictType 字典类型 * @param dictLabel 字典标签 * @param separator 分隔符 * @return 字典值 */ @SuppressWarnings("unchecked cast") @Override public String getDictValue(String dictType, String dictLabel, String separator) { // 优先从本地缓存获取 List datas = (List) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType); if (ObjectUtil.isNull(datas)) { datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas); } Map map = StreamUtils.toMap(datas, SysDictData::getDictLabel, SysDictData::getDictValue); if (StringUtils.containsAny(dictLabel, separator)) { return Arrays.stream(dictLabel.split(separator)) .map(l -> map.getOrDefault(l, StringUtils.EMPTY)) .collect(Collectors.joining(separator)); } else { return map.getOrDefault(dictLabel, StringUtils.EMPTY); } } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysLogininforServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.constant.Constants; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.event.LogininforEvent; import top.flya.common.core.page.TableDataInfo; import top.flya.common.utils.ServletUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.ip.AddressUtils; import top.flya.system.domain.SysLogininfor; import top.flya.system.mapper.SysLogininforMapper; import top.flya.system.service.ISysLogininforService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; /** * 系统访问日志情况信息 服务层处理 * * @author Lion Li */ @RequiredArgsConstructor @Slf4j @Service public class SysLogininforServiceImpl implements ISysLogininforService { private final SysLogininforMapper baseMapper; /** * 记录登录信息 * * @param logininforEvent 登录事件 */ @Async @EventListener public void recordLogininfor(LogininforEvent logininforEvent) { HttpServletRequest request = logininforEvent.getRequest(); final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); final String ip = ServletUtils.getClientIP(request); String address = AddressUtils.getRealAddressByIP(ip); StringBuilder s = new StringBuilder(); s.append(getBlock(ip)); s.append(address); s.append(getBlock(logininforEvent.getUsername())); s.append(getBlock(logininforEvent.getStatus())); s.append(getBlock(logininforEvent.getMessage())); // 打印信息到日志 log.info(s.toString(), logininforEvent.getArgs()); // 获取客户端操作系统 String os = userAgent.getOs().getName(); // 获取客户端浏览器 String browser = userAgent.getBrowser().getName(); // 封装对象 SysLogininfor logininfor = new SysLogininfor(); logininfor.setUserName(logininforEvent.getUsername()); logininfor.setIpaddr(ip); logininfor.setLoginLocation(address); logininfor.setBrowser(browser); logininfor.setOs(os); logininfor.setMsg(logininforEvent.getMessage()); // 日志状态 if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { logininfor.setStatus(Constants.SUCCESS); } else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) { logininfor.setStatus(Constants.FAIL); } // 插入数据 insertLogininfor(logininfor); } private String getBlock(Object msg) { if (msg == null) { msg = ""; } return "[" + msg.toString() + "]"; } @Override public TableDataInfo selectPageLogininforList(SysLogininfor logininfor, PageQuery pageQuery) { Map params = logininfor.getParams(); LambdaQueryWrapper lqw = new LambdaQueryWrapper() .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")); if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { pageQuery.setOrderByColumn("info_id"); pageQuery.setIsAsc("desc"); } Page page = baseMapper.selectPage(pageQuery.build(), lqw); return TableDataInfo.build(page); } /** * 新增系统登录日志 * * @param logininfor 访问日志对象 */ @Override public void insertLogininfor(SysLogininfor logininfor) { logininfor.setLoginTime(new Date()); baseMapper.insert(logininfor); } /** * 查询系统登录日志集合 * * @param logininfor 访问日志对象 * @return 登录记录集合 */ @Override public List selectLogininforList(SysLogininfor logininfor) { Map params = logininfor.getParams(); return baseMapper.selectList(new LambdaQueryWrapper() .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")) .orderByDesc(SysLogininfor::getInfoId)); } /** * 批量删除系统登录日志 * * @param infoIds 需要删除的登录日志ID * @return 结果 */ @Override public int deleteLogininforByIds(Long[] infoIds) { return baseMapper.deleteBatchIds(Arrays.asList(infoIds)); } /** * 清空系统登录日志 */ @Override public void cleanLogininfor() { baseMapper.delete(new LambdaQueryWrapper<>()); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysMenuServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import top.flya.common.constant.Constants; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.entity.SysMenu; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.TreeBuildUtils; import top.flya.system.domain.SysRoleMenu; import top.flya.system.domain.vo.MetaVo; import top.flya.system.domain.vo.RouterVo; import top.flya.system.mapper.SysMenuMapper; import top.flya.system.mapper.SysRoleMapper; import top.flya.system.mapper.SysRoleMenuMapper; import top.flya.system.service.ISysMenuService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.*; /** * 菜单 业务层处理 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysMenuServiceImpl implements ISysMenuService { private final SysMenuMapper baseMapper; private final SysRoleMapper roleMapper; private final SysRoleMenuMapper roleMenuMapper; /** * 根据用户查询系统菜单列表 * * @param userId 用户ID * @return 菜单列表 */ @Override public List selectMenuList(Long userId) { return selectMenuList(new SysMenu(), userId); } /** * 查询系统菜单列表 * * @param menu 菜单信息 * @return 菜单列表 */ @Override public List selectMenuList(SysMenu menu, Long userId) { List menuList = null; // 管理员显示所有菜单信息 if (LoginHelper.isAdmin(userId)) { menuList = baseMapper.selectList(new LambdaQueryWrapper() .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) .orderByAsc(SysMenu::getParentId) .orderByAsc(SysMenu::getOrderNum)); } else { QueryWrapper wrapper = Wrappers.query(); wrapper.eq("sur.user_id", userId) .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) .orderByAsc("m.parent_id") .orderByAsc("m.order_num"); menuList = baseMapper.selectMenuListByUserId(wrapper); } return menuList; } /** * 根据用户ID查询权限 * * @param userId 用户ID * @return 权限列表 */ @Override public Set selectMenuPermsByUserId(Long userId) { List perms = baseMapper.selectMenuPermsByUserId(userId); Set permsSet = new HashSet<>(); for (String perm : perms) { if (StringUtils.isNotEmpty(perm)) { permsSet.addAll(StringUtils.splitList(perm.trim())); } } return permsSet; } /** * 根据角色ID查询权限 * * @param roleId 角色ID * @return 权限列表 */ @Override public Set selectMenuPermsByRoleId(Long roleId) { List perms = baseMapper.selectMenuPermsByRoleId(roleId); Set permsSet = new HashSet<>(); for (String perm : perms) { if (StringUtils.isNotEmpty(perm)) { permsSet.addAll(StringUtils.splitList(perm.trim())); } } return permsSet; } /** * 根据用户ID查询菜单 * * @param userId 用户名称 * @return 菜单列表 */ @Override public List selectMenuTreeByUserId(Long userId) { List menus = null; if (LoginHelper.isAdmin(userId)) { menus = baseMapper.selectMenuTreeAll(); } else { menus = baseMapper.selectMenuTreeByUserId(userId); } return getChildPerms(menus, 0); } /** * 根据角色ID查询菜单树信息 * * @param roleId 角色ID * @return 选中菜单列表 */ @Override public List selectMenuListByRoleId(Long roleId) { SysRole role = roleMapper.selectById(roleId); return baseMapper.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly()); } /** * 构建前端路由所需要的菜单 * * @param menus 菜单列表 * @return 路由列表 */ @Override public List buildMenus(List menus) { List routers = new LinkedList<>(); 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.getQueryParam()); router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); List cMenus = menu.getChildren(); if (CollUtil.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 childrenList = new ArrayList<>(); RouterVo children = new RouterVo(); children.setPath(menu.getPath()); children.setComponent(menu.getComponent()); children.setName(StringUtils.capitalize(menu.getPath())); children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); children.setQuery(menu.getQueryParam()); 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 childrenList = new ArrayList<>(); RouterVo children = new RouterVo(); String routerPath = innerLinkReplaceEach(menu.getPath()); children.setPath(routerPath); children.setComponent(UserConstants.INNER_LINK); children.setName(StringUtils.capitalize(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> buildMenuTreeSelect(List menus) { if (CollUtil.isEmpty(menus)) { return CollUtil.newArrayList(); } return TreeBuildUtils.build(menus, (menu, tree) -> tree.setId(menu.getMenuId()) .setParentId(menu.getParentId()) .setName(menu.getMenuName()) .setWeight(menu.getOrderNum())); } /** * 根据菜单ID查询信息 * * @param menuId 菜单ID * @return 菜单信息 */ @Override public SysMenu selectMenuById(Long menuId) { return baseMapper.selectById(menuId); } /** * 是否存在菜单子节点 * * @param menuId 菜单ID * @return 结果 */ @Override public boolean hasChildByMenuId(Long menuId) { return baseMapper.exists(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); } /** * 查询菜单使用数量 * * @param menuId 菜单ID * @return 结果 */ @Override public boolean checkMenuExistRole(Long menuId) { return roleMenuMapper.exists(new LambdaQueryWrapper().eq(SysRoleMenu::getMenuId, menuId)); } /** * 新增保存菜单信息 * * @param menu 菜单信息 * @return 结果 */ @Override public int insertMenu(SysMenu menu) { return baseMapper.insert(menu); } /** * 修改保存菜单信息 * * @param menu 菜单信息 * @return 结果 */ @Override public int updateMenu(SysMenu menu) { return baseMapper.updateById(menu); } /** * 删除菜单管理信息 * * @param menuId 菜单ID * @return 结果 */ @Override public int deleteMenuById(Long menuId) { return baseMapper.deleteById(menuId); } /** * 校验菜单名称是否唯一 * * @param menu 菜单信息 * @return 结果 */ @Override public boolean checkMenuNameUnique(SysMenu menu) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysMenu::getMenuName, menu.getMenuName()) .eq(SysMenu::getParentId, menu.getParentId()) .ne(ObjectUtil.isNotNull(menu.getMenuId()), SysMenu::getMenuId, menu.getMenuId())); return !exist; } /** * 获取路由名称 * * @param menu 菜单信息 * @return 路由名称 */ public String getRouteName(SysMenu menu) { String routerName = StringUtils.capitalize(menu.getPath()); // 非外链并且是一级目录(类型为目录) if (isMenuFrame(menu)) { routerName = StringUtils.EMPTY; } return 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 getChildPerms(List list, int parentId) { List returnList = new ArrayList<>(); for (SysMenu t : list) { // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 if (t.getParentId() == parentId) { recursionFn(list, t); returnList.add(t); } } return returnList; } /** * 递归列表 * * @param list * @param t */ private void recursionFn(List list, SysMenu t) { // 得到子节点列表 List childList = getChildList(list, t); t.setChildren(childList); for (SysMenu tChild : childList) { if (hasChild(list, tChild)) { recursionFn(list, tChild); } } } /** * 得到子节点列表 */ private List getChildList(List list, SysMenu t) { return StreamUtils.filter(list, n -> n.getParentId().equals(t.getMenuId())); } /** * 判断是否有子节点 */ private boolean hasChild(List list, SysMenu t) { return CollUtil.isNotEmpty(getChildList(list, t)); } /** * 内链域名特殊字符替换 */ public String innerLinkReplaceEach(String path) { return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, "."}, new String[]{"", "", "", "/"}); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysNoticeServiceImpl.java ================================================ package top.flya.system.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.utils.StringUtils; import top.flya.system.domain.SysNotice; import top.flya.system.mapper.SysNoticeMapper; import top.flya.system.service.ISysNoticeService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; /** * 公告 服务层实现 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysNoticeServiceImpl implements ISysNoticeService { private final SysNoticeMapper baseMapper; @Override public TableDataInfo selectPageNoticeList(SysNotice notice, PageQuery pageQuery) { LambdaQueryWrapper lqw = new LambdaQueryWrapper() .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle()) .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType()) // .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy()) ; Page page = baseMapper.selectPage(pageQuery.build(), lqw); return TableDataInfo.build(page); } /** * 查询公告信息 * * @param noticeId 公告ID * @return 公告信息 */ @Override public SysNotice selectNoticeById(Long noticeId) { return baseMapper.selectById(noticeId); } /** * 查询公告列表 * * @param notice 公告信息 * @return 公告集合 */ @Override public List selectNoticeList(SysNotice notice) { return baseMapper.selectList(new LambdaQueryWrapper() .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle()) .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())) // .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy())) ; } /** * 新增公告 * * @param notice 公告信息 * @return 结果 */ @Override public int insertNotice(SysNotice notice) { return baseMapper.insert(notice); } /** * 修改公告 * * @param notice 公告信息 * @return 结果 */ @Override public int updateNotice(SysNotice notice) { return baseMapper.updateById(notice); } /** * 删除公告对象 * * @param noticeId 公告ID * @return 结果 */ @Override public int deleteNoticeById(Long noticeId) { return baseMapper.deleteById(noticeId); } /** * 批量删除公告信息 * * @param noticeIds 需要删除的公告ID * @return 结果 */ @Override public int deleteNoticeByIds(Long[] noticeIds) { return baseMapper.deleteBatchIds(Arrays.asList(noticeIds)); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysOperLogServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ArrayUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.event.OperLogEvent; import top.flya.common.core.page.TableDataInfo; import top.flya.common.utils.StringUtils; import top.flya.common.utils.ip.AddressUtils; import top.flya.system.domain.SysOperLog; import top.flya.system.mapper.SysOperLogMapper; import top.flya.system.service.ISysOperLogService; import lombok.RequiredArgsConstructor; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; /** * 操作日志 服务层处理 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysOperLogServiceImpl implements ISysOperLogService { private final SysOperLogMapper baseMapper; /** * 操作日志记录 * * @param operLogEvent 操作日志事件 */ @Async @EventListener public void recordOper(OperLogEvent operLogEvent) { SysOperLog operLog = BeanUtil.toBean(operLogEvent, SysOperLog.class); // 远程查询操作地点 operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); insertOperlog(operLog); } @Override public TableDataInfo selectPageOperLogList(SysOperLog operLog, PageQuery pageQuery) { Map params = operLog.getParams(); LambdaQueryWrapper lqw = new LambdaQueryWrapper() .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, SysOperLog::getBusinessType, operLog.getBusinessType()) .func(f -> { if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); } }) .eq(operLog.getStatus() != null, SysOperLog::getStatus, operLog.getStatus()) .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")); if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { pageQuery.setOrderByColumn("oper_id"); pageQuery.setIsAsc("desc"); } Page page = baseMapper.selectPage(pageQuery.build(), lqw); return TableDataInfo.build(page); } /** * 新增操作日志 * * @param operLog 操作日志对象 */ @Override public void insertOperlog(SysOperLog operLog) { operLog.setOperTime(new Date()); baseMapper.insert(operLog); } /** * 查询系统操作日志集合 * * @param operLog 操作日志对象 * @return 操作日志集合 */ @Override public List selectOperLogList(SysOperLog operLog) { Map params = operLog.getParams(); return baseMapper.selectList(new LambdaQueryWrapper() .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, SysOperLog::getBusinessType, operLog.getBusinessType()) .func(f -> { if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); } }) .eq(operLog.getStatus() != null && operLog.getStatus() > 0, SysOperLog::getStatus, operLog.getStatus()) .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) .between(params.get("beginTime") != null && params.get("endTime") != null, SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")) .orderByDesc(SysOperLog::getOperId)); } /** * 批量删除系统操作日志 * * @param operIds 需要删除的操作日志ID * @return 结果 */ @Override public int deleteOperLogByIds(Long[] operIds) { return baseMapper.deleteBatchIds(Arrays.asList(operIds)); } /** * 查询操作日志详细 * * @param operId 操作ID * @return 操作日志对象 */ @Override public SysOperLog selectOperLogById(Long operId) { return baseMapper.selectById(operId); } /** * 清空操作日志 */ @Override public void cleanOperLog() { baseMapper.delete(new LambdaQueryWrapper<>()); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysOssConfigServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.constant.CacheNames; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.exception.ServiceException; import top.flya.common.utils.JsonUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.redis.CacheUtils; import top.flya.common.utils.redis.RedisUtils; import top.flya.oss.constant.OssConstant; import top.flya.system.domain.SysOssConfig; import top.flya.system.domain.bo.SysOssConfigBo; import top.flya.system.domain.vo.SysOssConfigVo; import top.flya.system.mapper.SysOssConfigMapper; import top.flya.system.service.ISysOssConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.List; /** * 对象存储配置Service业务层处理 * * @author Lion Li * @author 孤舟烟雨 * @date 2021-08-13 */ @Slf4j @RequiredArgsConstructor @Service public class SysOssConfigServiceImpl implements ISysOssConfigService { private final SysOssConfigMapper baseMapper; /** * 项目启动时,初始化参数到缓存,加载配置类 */ @Override public void init() { List list = baseMapper.selectList(); // 加载OSS初始化配置 for (SysOssConfig config : list) { String configKey = config.getConfigKey(); if ("0".equals(config.getStatus())) { RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey); } CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); } } @Override public SysOssConfigVo queryById(Long ossConfigId) { return baseMapper.selectVoById(ossConfigId); } @Override public TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } private LambdaQueryWrapper buildQueryWrapper(SysOssConfigBo bo) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getConfigKey()), SysOssConfig::getConfigKey, bo.getConfigKey()); lqw.like(StringUtils.isNotBlank(bo.getBucketName()), SysOssConfig::getBucketName, bo.getBucketName()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysOssConfig::getStatus, bo.getStatus()); return lqw; } @Override public Boolean insertByBo(SysOssConfigBo bo) { SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class); validEntityBeforeSave(config); boolean flag = baseMapper.insert(config) > 0; if (flag) { CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); } return flag; } @Override public Boolean updateByBo(SysOssConfigBo bo) { SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class); validEntityBeforeSave(config); LambdaUpdateWrapper luw = new LambdaUpdateWrapper<>(); luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, ""); luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, ""); luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, ""); luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, ""); luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId()); boolean flag = baseMapper.update(config, luw) > 0; if (flag) { CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); } return flag; } /** * 保存前的数据校验 */ private void validEntityBeforeSave(SysOssConfig entity) { if (StringUtils.isNotEmpty(entity.getConfigKey()) && !checkConfigKeyUnique(entity)) { throw new ServiceException("操作配置'" + entity.getConfigKey() + "'失败, 配置key已存在!"); } } @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { if (CollUtil.containsAny(ids, OssConstant.SYSTEM_DATA_IDS)) { throw new ServiceException("系统内置, 不可删除!"); } } List list = CollUtil.newArrayList(); for (Long configId : ids) { SysOssConfig config = baseMapper.selectById(configId); list.add(config); } boolean flag = baseMapper.deleteBatchIds(ids) > 0; if (flag) { list.forEach(sysOssConfig -> CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); } return flag; } /** * 判断configKey是否唯一 */ private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) { long ossConfigId = ObjectUtil.isNull(sysOssConfig.getOssConfigId()) ? -1L : sysOssConfig.getOssConfigId(); SysOssConfig info = baseMapper.selectOne(new LambdaQueryWrapper() .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey) .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey())); if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) { return false; } return true; } /** * 启用禁用状态 */ @Override @Transactional(rollbackFor = Exception.class) public int updateOssConfigStatus(SysOssConfigBo bo) { SysOssConfig sysOssConfig = BeanUtil.toBean(bo, SysOssConfig.class); int row = baseMapper.update(null, new LambdaUpdateWrapper() .set(SysOssConfig::getStatus, "1")); row += baseMapper.updateById(sysOssConfig); if (row > 0) { RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey()); } return row; } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysOssServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.convert.Convert; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.poi.ss.formula.functions.T; import top.flya.common.constant.CacheNames; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.service.OssService; import top.flya.common.exception.ServiceException; import top.flya.common.utils.BeanCopyUtils; import top.flya.common.utils.StringUtils; import top.flya.common.utils.file.FileUtils; import top.flya.common.utils.spring.SpringUtils; import top.flya.oss.core.OssClient; import top.flya.oss.entity.UploadResult; import top.flya.oss.enumd.AccessPolicyType; import top.flya.oss.factory.OssFactory; import top.flya.system.domain.SysOss; import top.flya.system.domain.bo.SysOssBo; import top.flya.system.domain.vo.SysOssVo; import top.flya.system.mapper.SysOssMapper; import top.flya.system.service.ISysOssService; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.Cacheable; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.stream.Collectors; /** * 文件上传 服务层实现 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysOssServiceImpl implements ISysOssService, OssService { private final SysOssMapper baseMapper; @Override public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); List filterResult = result.getRecords().stream().map(this::matchingUrl).collect(Collectors.toList()); result.setRecords(filterResult); return TableDataInfo.build(result); } @Override public List listByIds(Collection ossIds) { List list = new ArrayList<>(); for (Long id : ossIds) { SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); if (ObjectUtil.isNotNull(vo)) { list.add(this.matchingUrl(vo)); } } return list; } @Override public String selectUrlByIds(String ossIds) { List list = new ArrayList<>(); for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); if (ObjectUtil.isNotNull(vo)) { list.add(this.matchingUrl(vo).getUrl()); } } return String.join(StringUtils.SEPARATOR, list); } private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName()); lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName()); lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix()); lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl()); lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); // lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy()); lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService()); return lqw; } @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") @Override public SysOssVo getById(Long ossId) { return baseMapper.selectVoById(ossId); } @Override public void download(Long ossId, HttpServletResponse response) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); if (ObjectUtil.isNull(sysOss)) { throw new ServiceException("文件数据不存在!"); } FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); OssClient storage = OssFactory.instance(); try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { int available = inputStream.available(); IoUtil.copy(inputStream, response.getOutputStream(), available); response.setContentLength(available); } catch (Exception e) { throw new ServiceException(e.getMessage()); } } @Override public SysOssVo upload(MultipartFile file) { String originalfileName = file.getOriginalFilename(); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); } catch (IOException e) { throw new ServiceException(e.getMessage()); } // 保存文件信息 SysOss oss = new SysOss(); oss.setUrl(uploadResult.getUrl()); oss.setFileSuffix(suffix); oss.setFileName(uploadResult.getFilename()); oss.setOriginalName(originalfileName); oss.setService(storage.getConfigKey()); baseMapper.insert(oss); SysOssVo sysOssVo = new SysOssVo(); BeanCopyUtils.copy(oss, sysOssVo); return this.matchingUrl(sysOssVo); } @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { // 做一些业务上的校验,判断是否需要校验 } List list = baseMapper.selectBatchIds(ids); for (SysOss sysOss : list) { OssClient storage = OssFactory.instance(sysOss.getService()); storage.delete(sysOss.getUrl()); } return baseMapper.deleteBatchIds(ids) > 0; } /** * 匹配Url * * @param oss OSS对象 * @return oss 匹配Url的OSS对象 */ private SysOssVo matchingUrl(SysOssVo oss) { OssClient storage = OssFactory.instance(oss.getService()); // 仅修改桶类型为 private 的URL,临时URL时长为120s if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120)); } return oss; } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysPostServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.page.TableDataInfo; import top.flya.common.exception.ServiceException; import top.flya.common.utils.StringUtils; import top.flya.system.domain.SysPost; import top.flya.system.domain.SysUserPost; import top.flya.system.mapper.SysPostMapper; import top.flya.system.mapper.SysUserPostMapper; import top.flya.system.service.ISysPostService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; /** * 岗位信息 服务层处理 * * @author Lion Li */ @RequiredArgsConstructor @Service public class SysPostServiceImpl implements ISysPostService { private final SysPostMapper baseMapper; private final SysUserPostMapper userPostMapper; @Override public TableDataInfo selectPagePostList(SysPost post, PageQuery pageQuery) { LambdaQueryWrapper lqw = new LambdaQueryWrapper() .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode()) .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus()) .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName()); Page page = baseMapper.selectPage(pageQuery.build(), lqw); return TableDataInfo.build(page); } /** * 查询岗位信息集合 * * @param post 岗位信息 * @return 岗位信息集合 */ @Override public List selectPostList(SysPost post) { return baseMapper.selectList(new LambdaQueryWrapper() .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode()) .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus()) .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName())); } /** * 查询所有岗位 * * @return 岗位列表 */ @Override public List selectPostAll() { return baseMapper.selectList(); } /** * 通过岗位ID查询岗位信息 * * @param postId 岗位ID * @return 角色对象信息 */ @Override public SysPost selectPostById(Long postId) { return baseMapper.selectById(postId); } /** * 根据用户ID获取岗位选择框列表 * * @param userId 用户ID * @return 选中岗位ID列表 */ @Override public List selectPostListByUserId(Long userId) { return baseMapper.selectPostListByUserId(userId); } /** * 校验岗位名称是否唯一 * * @param post 岗位信息 * @return 结果 */ @Override public boolean checkPostNameUnique(SysPost post) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysPost::getPostName, post.getPostName()) .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); return !exist; } /** * 校验岗位编码是否唯一 * * @param post 岗位信息 * @return 结果 */ @Override public boolean checkPostCodeUnique(SysPost post) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysPost::getPostCode, post.getPostCode()) .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); return !exist; } /** * 通过岗位ID查询岗位使用数量 * * @param postId 岗位ID * @return 结果 */ @Override public long countUserPostById(Long postId) { return userPostMapper.selectCount(new LambdaQueryWrapper().eq(SysUserPost::getPostId, postId)); } /** * 删除岗位信息 * * @param postId 岗位ID * @return 结果 */ @Override public int deletePostById(Long postId) { return baseMapper.deleteById(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 baseMapper.deleteBatchIds(Arrays.asList(postIds)); } /** * 新增保存岗位信息 * * @param post 岗位信息 * @return 结果 */ @Override public int insertPost(SysPost post) { return baseMapper.insert(post); } /** * 修改保存岗位信息 * * @param post 岗位信息 * @return 结果 */ @Override public int updatePost(SysPost post) { return baseMapper.updateById(post); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysRoleServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.extern.slf4j.Slf4j; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.core.domain.model.LoginUser; import top.flya.common.core.page.TableDataInfo; import top.flya.common.exception.ServiceException; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.system.domain.SysRoleDept; import top.flya.system.domain.SysRoleMenu; import top.flya.system.domain.SysUserRole; import top.flya.system.mapper.SysRoleDeptMapper; import top.flya.system.mapper.SysRoleMapper; import top.flya.system.mapper.SysRoleMenuMapper; import top.flya.system.mapper.SysUserRoleMapper; import top.flya.system.service.ISysRoleService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; /** * 角色 业务层处理 * * @author Lion Li */ @RequiredArgsConstructor @Service @Slf4j public class SysRoleServiceImpl implements ISysRoleService { private final SysRoleMapper baseMapper; private final SysRoleMenuMapper roleMenuMapper; private final SysUserRoleMapper userRoleMapper; private final SysRoleDeptMapper roleDeptMapper; @Override public TableDataInfo selectPageRoleList(SysRole role, PageQuery pageQuery) { Page page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); return TableDataInfo.build(page); } /** * 根据条件分页查询角色数据 * * @param role 角色信息 * @return 角色数据集合信息 */ @Override public List selectRoleList(SysRole role) { return baseMapper.selectRoleList(this.buildQueryWrapper(role)); } private Wrapper buildQueryWrapper(SysRole role) { Map params = role.getParams(); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("r.del_flag", UserConstants.ROLE_NORMAL) .eq(ObjectUtil.isNotNull(role.getRoleId()), "r.role_id", role.getRoleId()) .like(StringUtils.isNotBlank(role.getRoleName()), "r.role_name", role.getRoleName()) .eq(StringUtils.isNotBlank(role.getStatus()), "r.status", role.getStatus()) .like(StringUtils.isNotBlank(role.getRoleKey()), "r.role_key", role.getRoleKey()) .between(params.get("beginTime") != null && params.get("endTime") != null, "r.create_time", params.get("beginTime"), params.get("endTime")) .orderByAsc("r.role_sort").orderByAsc("r.create_time"); return wrapper; } /** * 根据用户ID查询角色 * * @param userId 用户ID * @return 角色列表 */ @Override public List selectRolesByUserId(Long userId) { List userRoles = baseMapper.selectRolePermissionByUserId(userId); List 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 selectRolePermissionByUserId(Long userId) { List perms = baseMapper.selectRolePermissionByUserId(userId); Set permsSet = new HashSet<>(); for (SysRole perm : perms) { if (ObjectUtil.isNotNull(perm)) { permsSet.addAll(StringUtils.splitList(perm.getRoleKey().trim())); } } return permsSet; } /** * 查询所有角色 * * @return 角色列表 */ @Override public List selectRoleAll() { return this.selectRoleList(new SysRole()); } /** * 根据用户ID获取角色选择框列表 * * @param userId 用户ID * @return 选中角色ID列表 */ @Override public List selectRoleListByUserId(Long userId) { return baseMapper.selectRoleListByUserId(userId); } /** * 通过角色ID查询角色 * * @param roleId 角色ID * @return 角色对象信息 */ @Override public SysRole selectRoleById(Long roleId) { return baseMapper.selectById(roleId); } /** * 校验角色名称是否唯一 * * @param role 角色信息 * @return 结果 */ @Override public boolean checkRoleNameUnique(SysRole role) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysRole::getRoleName, role.getRoleName()) .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); return !exist; } /** * 校验角色权限是否唯一 * * @param role 角色信息 * @return 结果 */ @Override public boolean checkRoleKeyUnique(SysRole role) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysRole::getRoleKey, role.getRoleKey()) .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); return !exist; } /** * 校验角色是否允许操作 * * @param role 角色信息 */ @Override public void checkRoleAllowed(SysRole role) { if (ObjectUtil.isNotNull(role.getRoleId()) && role.isAdmin()) { throw new ServiceException("不允许操作超级管理员角色"); } } /** * 校验角色是否有数据权限 * * @param roleId 角色id */ @Override public void checkRoleDataScope(Long roleId) { if (!LoginHelper.isAdmin()) { SysRole role = new SysRole(); role.setRoleId(roleId); List roles = this.selectRoleList(role); if (CollUtil.isEmpty(roles)) { throw new ServiceException("没有权限访问角色数据!"); } } } /** * 通过角色ID查询角色使用数量 * * @param roleId 角色ID * @return 结果 */ @Override public long countUserRoleByRoleId(Long roleId) { return userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); } /** * 新增保存角色信息 * * @param role 角色信息 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int insertRole(SysRole role) { // 新增角色信息 baseMapper.insert(role); return insertRoleMenu(role); } /** * 修改保存角色信息 * * @param role 角色信息 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int updateRole(SysRole role) { // 修改角色信息 baseMapper.updateById(role); // 删除角色与菜单关联 roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, role.getRoleId())); return insertRoleMenu(role); } /** * 修改角色状态 * * @param role 角色信息 * @return 结果 */ @Override public int updateRoleStatus(SysRole role) { return baseMapper.updateById(role); } /** * 修改数据权限信息 * * @param role 角色信息 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int authDataScope(SysRole role) { // 修改角色信息 baseMapper.updateById(role); // 删除角色与部门关联 roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, role.getRoleId())); // 新增角色和部门信息(数据权限) return insertRoleDept(role); } /** * 新增角色菜单信息 * * @param role 角色对象 */ public int insertRoleMenu(SysRole role) { int rows = 1; // 新增用户与角色管理 List list = new ArrayList(); 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.insertBatch(list) ? list.size() : 0; } return rows; } /** * 新增角色部门信息(数据权限) * * @param role 角色对象 */ public int insertRoleDept(SysRole role) { int rows = 1; // 新增角色与部门(数据权限)管理 List list = new ArrayList(); 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.insertBatch(list) ? list.size() : 0; } return rows; } /** * 通过角色ID删除角色 * * @param roleId 角色ID * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int deleteRoleById(Long roleId) { // 删除角色与菜单关联 roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); // 删除角色与部门关联 roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, roleId)); return baseMapper.deleteById(roleId); } /** * 批量删除角色信息 * * @param roleIds 需要删除的角色ID * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) 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())); } } List ids = Arrays.asList(roleIds); // 删除角色与菜单关联 roleMenuMapper.delete(new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, ids)); // 删除角色与部门关联 roleDeptMapper.delete(new LambdaQueryWrapper().in(SysRoleDept::getRoleId, ids)); return baseMapper.deleteBatchIds(ids); } /** * 取消授权用户角色 * * @param userRole 用户和角色关联信息 * @return 结果 */ @Override public int deleteAuthUser(SysUserRole userRole) { int rows = userRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getRoleId, userRole.getRoleId()) .eq(SysUserRole::getUserId, userRole.getUserId())); if (rows > 0) { cleanOnlineUserByRole(userRole.getRoleId()); } return rows; } /** * 批量取消授权用户角色 * * @param roleId 角色ID * @param userIds 需要取消授权的用户数据ID * @return 结果 */ @Override public int deleteAuthUsers(Long roleId, Long[] userIds) { int rows = userRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getRoleId, roleId) .in(SysUserRole::getUserId, Arrays.asList(userIds))); if (rows > 0) { cleanOnlineUserByRole(roleId); } return rows; } /** * 批量选择授权用户角色 * * @param roleId 角色ID * @param userIds 需要授权的用户数据ID * @return 结果 */ @Override public int insertAuthUsers(Long roleId, Long[] userIds) { // 新增用户与角色管理 int rows = 1; List list = StreamUtils.toList(Arrays.asList(userIds), userId -> { SysUserRole ur = new SysUserRole(); ur.setUserId(userId); ur.setRoleId(roleId); return ur; }); if (CollUtil.isNotEmpty(list)) { rows = userRoleMapper.insertBatch(list) ? list.size() : 0; } if (rows > 0) { cleanOnlineUserByRole(roleId); } return rows; } @Override public void cleanOnlineUserByRole(Long roleId) { List keys = StpUtil.searchTokenValue("", 0, -1, false); if (CollUtil.isEmpty(keys)) { return; } log.info("清除角色为{}的在线用户,keys is {}", roleId, keys); // 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作 keys.parallelStream().forEach(key -> { String token = StringUtils.substringAfterLast(key, ":"); // 如果已经过期则跳过 if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) { return; } LoginUser loginUser = LoginHelper.getLoginUser(token); if(loginUser == null|loginUser.getRoles()==null){ return; } if (loginUser.getRoles().stream().anyMatch(r -> r.getRoleId().equals(roleId))) { try { StpUtil.logoutByTokenValue(token); } catch (NotLoginException ignored) { } } }); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysSensitiveServiceImpl.java ================================================ package top.flya.system.service.impl; import top.flya.common.core.service.SensitiveService; import top.flya.common.helper.LoginHelper; import org.springframework.stereotype.Service; /** * 脱敏服务 * 默认管理员不过滤 * 需自行根据业务重写实现 * * @author Lion Li * @version 3.6.0 */ @Service public class SysSensitiveServiceImpl implements SensitiveService { /** * 是否脱敏 */ @Override public boolean isSensitive() { return !LoginHelper.isAdmin(); } } ================================================ FILE: ruoyi-system/src/main/java/top/flya/system/service/impl/SysUserServiceImpl.java ================================================ package top.flya.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import top.flya.common.constant.CacheNames; import top.flya.common.constant.UserConstants; import top.flya.common.core.domain.PageQuery; import top.flya.common.core.domain.entity.SysDept; import top.flya.common.core.domain.entity.SysRole; import top.flya.common.core.domain.entity.SysUser; import top.flya.common.core.page.TableDataInfo; import top.flya.common.core.service.UserService; import top.flya.common.exception.ServiceException; import top.flya.common.helper.DataBaseHelper; import top.flya.common.helper.LoginHelper; import top.flya.common.utils.StreamUtils; import top.flya.common.utils.StringUtils; import top.flya.system.domain.SysPost; import top.flya.system.domain.SysUserPost; import top.flya.system.domain.SysUserRole; import top.flya.system.mapper.*; import top.flya.system.mapper.*; import top.flya.system.service.ISysUserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; import java.util.Map; /** * 用户 业务层处理 * * @author Lion Li */ @Slf4j @RequiredArgsConstructor @Service public class SysUserServiceImpl implements ISysUserService, UserService { private final SysUserMapper baseMapper; private final SysDeptMapper deptMapper; private final SysRoleMapper roleMapper; private final SysPostMapper postMapper; private final SysUserRoleMapper userRoleMapper; private final SysUserPostMapper userPostMapper; @Override public TableDataInfo selectPageUserList(SysUser user, PageQuery pageQuery) { Page page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user)); return TableDataInfo.build(page); } /** * 根据条件分页查询用户列表 * * @param user 用户信息 * @return 用户信息集合信息 */ @Override public List selectUserList(SysUser user) { return baseMapper.selectUserList(this.buildQueryWrapper(user)); } private Wrapper buildQueryWrapper(SysUser user) { Map params = user.getParams(); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) .between(params.get("beginTime") != null && params.get("endTime") != null, "u.create_time", params.get("beginTime"), params.get("endTime")) .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { List deptList = deptMapper.selectList(new LambdaQueryWrapper() .select(SysDept::getDeptId) .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors"))); List ids = StreamUtils.toList(deptList, SysDept::getDeptId); ids.add(user.getDeptId()); w.in("u.dept_id", ids); }); return wrapper; } /** * 根据条件分页查询已分配用户角色列表 * * @param user 用户信息 * @return 用户信息集合信息 */ @Override public TableDataInfo selectAllocatedList(SysUser user, PageQuery pageQuery) { QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()); Page page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } /** * 根据条件分页查询未分配用户角色列表 * * @param user 用户信息 * @return 用户信息集合信息 */ @Override public TableDataInfo selectUnallocatedList(SysUser user, PageQuery pageQuery) { List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()); Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); return TableDataInfo.build(page); } /** * 通过用户名查询用户 * * @param userName 用户名 * @return 用户对象信息 */ @Override public SysUser selectUserByUserName(String userName) { return baseMapper.selectUserByUserName(userName); } /** * 通过手机号查询用户 * * @param phonenumber 手机号 * @return 用户对象信息 */ @Override public SysUser selectUserByPhonenumber(String phonenumber) { return baseMapper.selectUserByPhonenumber(phonenumber); } /** * 通过用户ID查询用户 * * @param userId 用户ID * @return 用户对象信息 */ @Override public SysUser selectUserById(Long userId) { return baseMapper.selectUserById(userId); } /** * 查询用户所属角色组 * * @param userName 用户名 * @return 结果 */ @Override public String selectUserRoleGroup(String userName) { List list = roleMapper.selectRolesByUserName(userName); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } return StreamUtils.join(list, SysRole::getRoleName); } /** * 查询用户所属岗位组 * * @param userName 用户名 * @return 结果 */ @Override public String selectUserPostGroup(String userName) { List list = postMapper.selectPostsByUserName(userName); if (CollUtil.isEmpty(list)) { return StringUtils.EMPTY; } return StreamUtils.join(list, SysPost::getPostName); } /** * 校验用户名称是否唯一 * * @param user 用户信息 * @return 结果 */ @Override public boolean checkUserNameUnique(SysUser user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysUser::getUserName, user.getUserName()) .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } /** * 校验手机号码是否唯一 * * @param user 用户信息 */ @Override public boolean checkPhoneUnique(SysUser user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysUser::getPhonenumber, user.getPhonenumber()) .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } /** * 校验email是否唯一 * * @param user 用户信息 */ @Override public boolean checkEmailUnique(SysUser user) { boolean exist = baseMapper.exists(new LambdaQueryWrapper() .eq(SysUser::getEmail, user.getEmail()) .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); return !exist; } /** * 校验用户是否允许操作 * * @param user 用户信息 */ @Override public void checkUserAllowed(SysUser user) { if (ObjectUtil.isNotNull(user.getUserId()) && user.isAdmin()) { throw new ServiceException("不允许操作超级管理员用户"); } } /** * 校验用户是否有数据权限 * * @param userId 用户id */ @Override public void checkUserDataScope(Long userId) { if (!LoginHelper.isAdmin()) { SysUser user = new SysUser(); user.setUserId(userId); List users = this.selectUserList(user); if (CollUtil.isEmpty(users)) { throw new ServiceException("没有权限访问用户数据!"); } } } /** * 新增保存用户信息 * * @param user 用户信息 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int insertUser(SysUser user) { // 新增用户信息 int rows = baseMapper.insert(user); // 新增用户岗位关联 insertUserPost(user); // 新增用户与角色管理 insertUserRole(user); return rows; } /** * 注册用户信息 * * @param user 用户信息 * @return 结果 */ @Override public boolean registerUser(SysUser user) { // user.setCreateBy(user.getUserName()); // user.setUpdateBy(user.getUserName()); return baseMapper.insert(user) > 0; } /** * 修改保存用户信息 * * @param user 用户信息 * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int updateUser(SysUser user) { Long userId = user.getUserId(); // 删除用户与角色关联 userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); // 新增用户与角色管理 insertUserRole(user); // 删除用户与岗位关联 userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, userId)); // 新增用户与岗位管理 insertUserPost(user); return baseMapper.updateById(user); } /** * 用户授权角色 * * @param userId 用户ID * @param roleIds 角色组 */ @Override @Transactional(rollbackFor = Exception.class) public void insertUserAuth(Long userId, Long[] roleIds) { userRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getUserId, userId)); insertUserRole(userId, roleIds); } /** * 修改用户状态 * * @param user 用户信息 * @return 结果 */ @Override public int updateUserStatus(SysUser user) { return baseMapper.updateById(user); } /** * 修改用户基本信息 * * @param user 用户信息 * @return 结果 */ @Override public int updateUserProfile(SysUser user) { return baseMapper.updateById(user); } /** * 修改用户头像 * * @param userName 用户名 * @param avatar 头像地址 * @return 结果 */ @Override public boolean updateUserAvatar(String userName, String avatar) { return baseMapper.update(null, new LambdaUpdateWrapper() .set(SysUser::getAvatar, avatar) .eq(SysUser::getUserName, userName)) > 0; } /** * 重置用户密码 * * @param user 用户信息 * @return 结果 */ @Override public int resetPwd(SysUser user) { return baseMapper.updateById(user); } /** * 重置用户密码 * * @param userName 用户名 * @param password 密码 * @return 结果 */ @Override public int resetUserPwd(String userName, String password) { return baseMapper.update(null, new LambdaUpdateWrapper() .set(SysUser::getPassword, password) .eq(SysUser::getUserName, userName)); } /** * 新增用户角色信息 * * @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 (ArrayUtil.isNotEmpty(posts)) { // 新增用户与岗位管理 List list = StreamUtils.toList(Arrays.asList(posts), postId -> { SysUserPost up = new SysUserPost(); up.setUserId(user.getUserId()); up.setPostId(postId); return up; }); userPostMapper.insertBatch(list); } } /** * 新增用户角色信息 * * @param userId 用户ID * @param roleIds 角色组 */ public void insertUserRole(Long userId, Long[] roleIds) { if (ArrayUtil.isNotEmpty(roleIds)) { // 新增用户与角色管理 List list = StreamUtils.toList(Arrays.asList(roleIds), roleId -> { SysUserRole ur = new SysUserRole(); ur.setUserId(userId); ur.setRoleId(roleId); return ur; }); userRoleMapper.insertBatch(list); } } /** * 通过用户ID删除用户 * * @param userId 用户ID * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int deleteUserById(Long userId) { // 删除用户与角色关联 userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); // 删除用户与岗位表 userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, userId)); return baseMapper.deleteById(userId); } /** * 批量删除用户信息 * * @param userIds 需要删除的用户ID * @return 结果 */ @Override @Transactional(rollbackFor = Exception.class) public int deleteUserByIds(Long[] userIds) { for (Long userId : userIds) { checkUserAllowed(new SysUser(userId)); checkUserDataScope(userId); } List ids = Arrays.asList(userIds); // 删除用户与角色关联 userRoleMapper.delete(new LambdaQueryWrapper().in(SysUserRole::getUserId, ids)); // 删除用户与岗位表 userPostMapper.delete(new LambdaQueryWrapper().in(SysUserPost::getUserId, ids)); return baseMapper.deleteBatchIds(ids); } @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") @Override public String selectUserNameById(Long userId) { SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName(); } } ================================================ FILE: ruoyi-system/src/main/resources/mapper/package-info.md ================================================ java包使用 `.` 分割 resource 目录使用 `/` 分割
        此文件目的 防止文件夹粘连找不到 `xml` 文件 ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml ================================================ 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 sur on sur.role_id = r.role_id left join sys_user u on u.user_id = sur.user_id left join sys_dept d on u.dept_id = d.dept_id ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml ================================================ select u.user_id, u.dept_id, u.user_name, u.nick_name, u.user_type, 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 sur on u.user_id = sur.user_id left join sys_role r on r.role_id = sur.role_id ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml ================================================ ================================================ FILE: ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml ================================================ ================================================ FILE: ruoyi-ui/.editorconfig ================================================ # 告诉EditorConfig插件,这是根文件,不用继续往上查找 root = true # 匹配全部文件 [*] # 设置字符集 charset = utf-8 # 缩进风格,可选space、tab indent_style = space # 缩进的空格数 indent_size = 2 # 结尾换行符,可选lf、cr、crlf end_of_line = lf # 在文件结尾插入新行 insert_final_newline = true # 删除一行中的前后空格 trim_trailing_whitespace = true # 匹配md结尾的文件 [*.md] insert_final_newline = false trim_trailing_whitespace = false ================================================ FILE: ruoyi-ui/.eslintignore ================================================ # 忽略build目录下类型为js的文件的语法检查 build/*.js # 忽略src/assets目录下文件的语法检查 src/assets # 忽略public目录下文件的语法检查 public # 忽略当前目录下为js的文件的语法检查 *.js # 忽略当前目录下为vue的文件的语法检查 *.vue ================================================ FILE: ruoyi-ui/.eslintrc.js ================================================ // ESlint 检查配置 module.exports = { root: true, parserOptions: { parser: 'babel-eslint', sourceType: 'module' }, env: { browser: true, node: true, es6: true, }, extends: ['plugin:vue/recommended', 'eslint:recommended'], // add your custom rules here //it is base on https://github.com/vuejs/eslint-config-vue rules: { "vue/max-attributes-per-line": [2, { "singleline": 10, "multiline": { "max": 1, "allowFirstLine": false } }], "vue/singleline-html-element-content-newline": "off", "vue/multiline-html-element-content-newline":"off", "vue/name-property-casing": ["error", "PascalCase"], "vue/no-v-html": "off", 'accessor-pairs': 2, 'arrow-spacing': [2, { 'before': true, 'after': true }], 'block-spacing': [2, 'always'], 'brace-style': [2, '1tbs', { 'allowSingleLine': true }], 'camelcase': [0, { 'properties': 'always' }], 'comma-dangle': [2, 'never'], 'comma-spacing': [2, { 'before': false, 'after': true }], 'comma-style': [2, 'last'], 'constructor-super': 2, 'curly': [2, 'multi-line'], 'dot-location': [2, 'property'], 'eol-last': 2, 'eqeqeq': ["error", "always", {"null": "ignore"}], 'generator-star-spacing': [2, { 'before': true, 'after': true }], 'handle-callback-err': [2, '^(err|error)$'], 'indent': [2, 2, { 'SwitchCase': 1 }], 'jsx-quotes': [2, 'prefer-single'], 'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }], 'keyword-spacing': [2, { 'before': true, 'after': true }], 'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }], 'new-parens': 2, 'no-array-constructor': 2, 'no-caller': 2, 'no-console': 'off', 'no-class-assign': 2, 'no-cond-assign': 2, 'no-const-assign': 2, 'no-control-regex': 0, 'no-delete-var': 2, 'no-dupe-args': 2, 'no-dupe-class-members': 2, 'no-dupe-keys': 2, 'no-duplicate-case': 2, 'no-empty-character-class': 2, 'no-empty-pattern': 2, 'no-eval': 2, 'no-ex-assign': 2, 'no-extend-native': 2, 'no-extra-bind': 2, 'no-extra-boolean-cast': 2, 'no-extra-parens': [2, 'functions'], 'no-fallthrough': 2, 'no-floating-decimal': 2, 'no-func-assign': 2, 'no-implied-eval': 2, 'no-inner-declarations': [2, 'functions'], 'no-invalid-regexp': 2, 'no-irregular-whitespace': 2, 'no-iterator': 2, 'no-label-var': 2, 'no-labels': [2, { 'allowLoop': false, 'allowSwitch': false }], 'no-lone-blocks': 2, 'no-mixed-spaces-and-tabs': 2, 'no-multi-spaces': 2, 'no-multi-str': 2, 'no-multiple-empty-lines': [2, { 'max': 1 }], 'no-native-reassign': 2, 'no-negated-in-lhs': 2, 'no-new-object': 2, 'no-new-require': 2, 'no-new-symbol': 2, 'no-new-wrappers': 2, 'no-obj-calls': 2, 'no-octal': 2, 'no-octal-escape': 2, 'no-path-concat': 2, 'no-proto': 2, 'no-redeclare': 2, 'no-regex-spaces': 2, 'no-return-assign': [2, 'except-parens'], 'no-self-assign': 2, 'no-self-compare': 2, 'no-sequences': 2, 'no-shadow-restricted-names': 2, 'no-spaced-func': 2, 'no-sparse-arrays': 2, 'no-this-before-super': 2, 'no-throw-literal': 2, 'no-trailing-spaces': 2, 'no-undef': 2, 'no-undef-init': 2, 'no-unexpected-multiline': 2, 'no-unmodified-loop-condition': 2, 'no-unneeded-ternary': [2, { 'defaultAssignment': false }], 'no-unreachable': 2, 'no-unsafe-finally': 2, 'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }], 'no-useless-call': 2, 'no-useless-computed-key': 2, 'no-useless-constructor': 2, 'no-useless-escape': 0, 'no-whitespace-before-property': 2, 'no-with': 2, 'one-var': [2, { 'initialized': 'never' }], 'operator-linebreak': [2, 'after', { 'overrides': { '?': 'before', ':': 'before' } }], 'padded-blocks': [2, 'never'], 'quotes': [2, 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], 'semi': [2, 'never'], 'semi-spacing': [2, { 'before': false, 'after': true }], 'space-before-blocks': [2, 'always'], 'space-before-function-paren': [2, 'never'], 'space-in-parens': [2, 'never'], 'space-infix-ops': 2, 'space-unary-ops': [2, { 'words': true, 'nonwords': false }], 'spaced-comment': [2, 'always', { 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }], 'template-curly-spacing': [2, 'never'], 'use-isnan': 2, 'valid-typeof': 2, 'wrap-iife': [2, 'any'], 'yield-star-spacing': [2, 'both'], 'yoda': [2, 'never'], 'prefer-const': 2, 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 'object-curly-spacing': [2, 'always', { objectsInObjects: false }], 'array-bracket-spacing': [2, 'never'] } } ================================================ FILE: ruoyi-ui/.gitignore ================================================ .DS_Store node_modules/ dist/ npm-debug.log* yarn-debug.log* yarn-error.log* **/*.log tests/**/coverage/ tests/e2e/reports selenium-debug.log # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.local package-lock.json yarn.lock ================================================ FILE: ruoyi-ui/Dockerfile ================================================ # 基础镜像 FROM node:14.15.1-alpine as build-stage # 设置工作目录 WORKDIR /app # 拷贝 package.json 和 package-lock.json 到工作目录 COPY ./ruoyi-ui/package*.json ./ # 设置npm的镜像源为淘宝镜像 RUN npm config set registry https://registry.npm.taobao.org/ # 安装依赖 RUN npm install # 拷贝所有文件到工作目录 COPY ./ruoyi-ui . # 构建生产环境的代码 RUN npm run build:prod # 生产环境镜像 FROM nginx:1.19.6-alpine # 将构建好的代码拷贝到 Nginx 的默认目录 COPY --from=build-stage /app/dist /usr/share/nginx/html # 复制自定义 Nginx 配置文件到容器中 COPY ./ruoyi-ui/nginx.conf /etc/nginx/conf.d/default.conf # 暴露80端口 EXPOSE 80 # 启动Nginx服务 CMD ["nginx", "-g", "daemon off;"] ================================================ FILE: ruoyi-ui/README.md ================================================ ## 开发 ```bash # 克隆项目 git clone https://gitee.com/y_project/RuoYi-Vue # 进入项目目录 cd ruoyi-ui # 安装依赖 npm install # 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 npm install --registry=https://registry.npmmirror.com # 启动服务 npm run dev ``` 浏览器访问 http://localhost:80 ## 发布 ```bash # 构建测试环境 npm run build:stage # 构建生产环境 npm run build:prod ``` ================================================ FILE: ruoyi-ui/babel.config.js ================================================ module.exports = { presets: [ // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app '@vue/cli-plugin-babel/preset' ], 'env': { 'development': { // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. 'plugins': ['dynamic-import-node'] } } } ================================================ FILE: ruoyi-ui/bin/build.bat ================================================ @echo off echo. echo [Ϣ] Weḅdistļ echo. %~d0 cd %~dp0 cd .. npm run build:prod pause ================================================ FILE: ruoyi-ui/bin/package.bat ================================================ @echo off echo. echo [信息] 安装Web工程,生成node_modules文件。 echo. %~d0 cd %~dp0 cd .. npm install --registry=https://registry.npmmirror.com pause ================================================ FILE: ruoyi-ui/bin/run-web.bat ================================================ @echo off echo. echo [信息] 使用 Vue CLI 命令运行 Web 工程。 echo. %~d0 cd %~dp0 cd .. npm run dev pause ================================================ FILE: ruoyi-ui/build/index.js ================================================ const { run } = require('runjs') const chalk = require('chalk') const config = require('../vue.config.js') const rawArgv = process.argv.slice(2) const args = rawArgv.join(' ') if (process.env.npm_config_preview || rawArgv.includes('--preview')) { const report = rawArgv.includes('--report') run(`vue-cli-service build ${args}`) const port = 9526 const publicPath = config.publicPath var connect = require('connect') var serveStatic = require('serve-static') const app = connect() app.use( publicPath, serveStatic('./dist', { index: ['index.html', '/'] }) ) app.listen(port, function () { console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) if (report) { console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) } }) } else { run(`vue-cli-service build ${args}`) } ================================================ FILE: ruoyi-ui/nginx.conf ================================================ # nginx配置 server { listen 80; server_name localhost; client_max_body_size 100m; #主要是这个参数,限制了上传文件大大小 #charset koi8-r; access_log /var/log/nginx/host.access.log main; error_log /var/log/nginx/error.log error; location / { # root 根目录,默认nginx镜像的html文件夹,可以指定其他 root /usr/share/nginx/html; index index.html index.htm; # 如果vue-router使用的是history模式,需要设置这个 try_files $uri $uri/ /index.html; } location /prod-api { rewrite ^/prod-api(.*)$ $1 break; proxy_pass http://paizhicheng-backend-service:9393; } # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ================================================ FILE: ruoyi-ui/package.json ================================================ { "name": "ruoyi-vue-plus", "version": "4.7.0", "description": "派之城后台管理系统", "author": "LionLi", "license": "MIT", "scripts": { "dev": "vue-cli-service serve", "build:prod": "vue-cli-service build", "preview": "node build/index.js --preview", "lint": "eslint --ext .js,.vue src" }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "src/**/*.{js,vue}": [ "eslint --fix", "git add" ] }, "keywords": [ "vue", "admin", "dashboard", "element-ui", "boilerplate", "admin-template", "management-system" ], "repository": { "type": "git", "url": "https://gitee.com/dromara/RuoYi-Vue-Plus.git" }, "dependencies": { "@amap/amap-jsapi-loader": "^1.0.1", "@riophae/vue-treeselect": "0.4.0", "axios": "0.24.0", "clipboard": "2.0.8", "cnpm": "^9.2.0", "core-js": "3.25.3", "dayjs": "^1.11.9", "echarts": "5.4.0", "element-ui": "2.15.12", "file-saver": "2.0.5", "fuse.js": "6.4.3", "highlight.js": "9.18.5", "js-beautify": "1.13.0", "js-cookie": "3.0.1", "jsencrypt": "3.0.0-rc.1", "nprogress": "0.2.0", "quill": "1.3.7", "screenfull": "5.0.2", "sortablejs": "1.10.2", "vue": "2.6.12", "vue-amap": "^0.5.10", "vue-count-to": "1.0.13", "vue-cropper": "0.5.5", "vue-meta": "2.4.0", "vue-router": "3.4.9", "vuedraggable": "2.24.3", "vuex": "3.6.0" }, "devDependencies": { "@vue/cli-plugin-babel": "4.4.6", "@vue/cli-plugin-eslint": "4.4.6", "@vue/cli-service": "4.4.6", "babel-eslint": "10.1.0", "babel-plugin-dynamic-import-node": "2.3.3", "chalk": "4.1.0", "compression-webpack-plugin": "5.0.2", "connect": "3.6.6", "eslint": "7.15.0", "eslint-plugin-vue": "7.2.0", "lint-staged": "10.5.3", "runjs": "4.4.2", "sass": "1.32.13", "sass-loader": "10.1.1", "script-ext-html-webpack-plugin": "2.1.5", "svg-sprite-loader": "5.1.1", "vue-template-compiler": "2.6.12" }, "engines": { "node": ">=8.9", "npm": ">= 3.0.0" }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: ruoyi-ui/public/html/ie.html ================================================ 请升级您的浏览器

        请升级您的浏览器,以便我们更好的为您提供服务!

        您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。


        请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

        自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明


        您可以选择更先进的浏览器

        推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。


        ================================================ FILE: ruoyi-ui/public/index.html ================================================ <%= webpackConfig.name %>
        正在加载系统资源,请耐心等待
        ================================================ FILE: ruoyi-ui/public/robots.txt ================================================ User-agent: * Disallow: / ================================================ FILE: ruoyi-ui/src/App.vue ================================================ ================================================ FILE: ruoyi-ui/src/api/demo/demo.js ================================================ import request from '@/utils/request' // 查询测试单表列表 export function listDemo(query) { return request({ url: '/demo/demo/list', method: 'get', params: query }) } // 自定义分页接口 export function pageDemo(query) { return request({ url: '/demo/demo/page', method: 'get', params: query }) } // 查询测试单表详细 export function getDemo(id) { return request({ url: '/demo/demo/' + id, method: 'get' }) } // 新增测试单表 export function addDemo(data) { return request({ url: '/demo/demo', method: 'post', data: data }) } // 修改测试单表 export function updateDemo(data) { return request({ url: '/demo/demo', method: 'put', data: data }) } // 删除测试单表 export function delDemo(id) { return request({ url: '/demo/demo/' + id, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/demo/tree.js ================================================ import request from '@/utils/request' // 查询测试树表列表 export function listTree(query) { return request({ url: '/demo/tree/list', method: 'get', params: query }) } // 查询测试树表详细 export function getTree(id) { return request({ url: '/demo/tree/' + id, method: 'get' }) } // 新增测试树表 export function addTree(data) { return request({ url: '/demo/tree', method: 'post', data: data }) } // 修改测试树表 export function updateTree(data) { return request({ url: '/demo/tree', method: 'put', data: data }) } // 删除测试树表 export function delTree(id) { return request({ url: '/demo/tree/' + id, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/login.js ================================================ import request from '@/utils/request' // 登录方法 export function login(username, password, code, uuid) { const data = { username, password, code, uuid } return request({ url: '/login', headers: { isToken: false }, method: 'post', data: data }) } // 注册方法 export function register(data) { return request({ url: '/register', headers: { isToken: false }, method: 'post', data: data }) } // 获取用户详细信息 export function getInfo() { return request({ url: '/getInfo', method: 'get' }) } // 退出方法 export function logout() { return request({ url: '/logout', method: 'post' }) } // 获取验证码 export function getCodeImg() { return request({ url: '/captchaImage', headers: { isToken: false }, method: 'get', timeout: 20000 }) } // 短信验证码 export function getCodeSms() { return request({ url: '/captchaSms', headers: { isToken: false }, method: 'get', timeout: 20000 }) } ================================================ FILE: ruoyi-ui/src/api/menu.js ================================================ import request from '@/utils/request' // 获取路由 export const getRouters = () => { return request({ url: '/getRouters', method: 'get' }) } ================================================ FILE: ruoyi-ui/src/api/monitor/cache.js ================================================ import request from '@/utils/request' // 查询缓存详细 export function getCache() { return request({ url: '/monitor/cache', method: 'get' }) } // 查询缓存名称列表 export function listCacheName() { return request({ url: '/monitor/cache/getNames', method: 'get' }) } // 查询缓存键名列表 export function listCacheKey(cacheName) { return request({ url: '/monitor/cache/getKeys/' + cacheName, method: 'get' }) } // 查询缓存内容 export function getCacheValue(cacheName, cacheKey) { return request({ url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, method: 'get' }) } // 清理指定名称缓存 export function clearCacheName(cacheName) { return request({ url: '/monitor/cache/clearCacheName/' + cacheName, method: 'delete' }) } // 清理指定键名缓存 export function clearCacheKey(cacheName, cacheKey) { return request({ url: '/monitor/cache/clearCacheKey/'+ cacheName + "/" + cacheKey, method: 'delete' }) } // 清理全部缓存 export function clearCacheAll() { return request({ url: '/monitor/cache/clearCacheAll', method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/monitor/logininfor.js ================================================ import request from '@/utils/request' // 查询登录日志列表 export function list(query) { return request({ url: '/monitor/logininfor/list', method: 'get', params: query }) } // 删除登录日志 export function delLogininfor(infoId) { return request({ url: '/monitor/logininfor/' + infoId, method: 'delete' }) } // 解锁用户登录状态 export function unlockLogininfor(userName) { return request({ url: '/monitor/logininfor/unlock/' + userName, method: 'get' }) } // 清空登录日志 export function cleanLogininfor() { return request({ url: '/monitor/logininfor/clean', method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/monitor/online.js ================================================ import request from '@/utils/request' // 查询在线用户列表 export function list(query) { return request({ url: '/monitor/online/list', method: 'get', params: query }) } // 强退用户 export function forceLogout(tokenId) { return request({ url: '/monitor/online/' + tokenId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/monitor/operlog.js ================================================ import request from '@/utils/request' // 查询操作日志列表 export function list(query) { return request({ url: '/monitor/operlog/list', method: 'get', params: query }) } // 删除操作日志 export function delOperlog(operId) { return request({ url: '/monitor/operlog/' + operId, method: 'delete' }) } // 清空操作日志 export function cleanOperlog() { return request({ url: '/monitor/operlog/clean', method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/activity.js ================================================ import request from '@/utils/request' // 查询活动列表 export function listActivity(query) { return request({ url: '/system/activity/list', method: 'get', params: query }) } // 查询活动详细 export function getActivity(activityId) { return request({ url: '/system/activity/' + activityId, method: 'get' }) } // 新增活动 export function addActivity(data) { return request({ url: '/system/activity', method: 'post', data: data }) } // 修改活动 export function updateActivity(data) { return request({ url: '/system/activity', method: 'put', data: data }) } // 删除活动 export function delActivity(activityId) { return request({ url: '/system/activity/' + activityId, method: 'delete' }) } // 获取地址列表 export function getAddressList() { return request({ url: '/system/region/list', method: 'get' }) } ================================================ FILE: ruoyi-ui/src/api/system/activityConnArtist.js ================================================ import request from '@/utils/request' // 查询活动关联艺人列表 export function listActivityConnArtist(query) { return request({ url: '/system/activityConnArtist/list', method: 'get', params: query }) } // 查询活动关联艺人详细 export function getActivityConnArtist(activityConnArtistId) { return request({ url: '/system/activityConnArtist/' + activityConnArtistId, method: 'get' }) } // 新增活动关联艺人 export function addActivityConnArtist(data) { return request({ url: '/system/activityConnArtist', method: 'post', data: data }) } // 修改活动关联艺人 export function updateActivityConnArtist(data) { return request({ url: '/system/activityConnArtist', method: 'put', data: data }) } // 删除活动关联艺人 export function delActivityConnArtist(activityConnArtistId) { return request({ url: '/system/activityConnArtist/' + activityConnArtistId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/activityConnIntro.js ================================================ import request from '@/utils/request' // 查询活动介绍与活动关联列表 export function listActivityConnIntro(query) { return request({ url: '/system/activityConnIntro/list', method: 'get', params: query }) } // 查询活动介绍与活动关联详细 export function getActivityConnIntro(activityConnIntroId) { return request({ url: '/system/activityConnIntro/' + activityConnIntroId, method: 'get' }) } // 新增活动介绍与活动关联 export function addActivityConnIntro(data) { return request({ url: '/system/activityConnIntro', method: 'post', data: data }) } // 修改活动介绍与活动关联 export function updateActivityConnIntro(data) { return request({ url: '/system/activityConnIntro', method: 'put', data: data }) } // 删除活动介绍与活动关联 export function delActivityConnIntro(activityConnIntroId) { return request({ url: '/system/activityConnIntro/' + activityConnIntroId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/activityConnTag.js ================================================ import request from '@/utils/request' // 查询活动标签与活动关联列表 export function listActivityConnTag(query) { return request({ url: '/system/activityConnTag/list', method: 'get', params: query }) } // 查询活动标签与活动关联详细 export function getActivityConnTag(activityConnTagId) { return request({ url: '/system/activityConnTag/' + activityConnTagId, method: 'get' }) } // 新增活动标签与活动关联 export function addActivityConnTag(data) { return request({ url: '/system/activityConnTag', method: 'post', data: data }) } // 修改活动标签与活动关联 export function updateActivityConnTag(data) { return request({ url: '/system/activityConnTag', method: 'put', data: data }) } // 删除活动标签与活动关联 export function delActivityConnTag(activityConnTagId) { return request({ url: '/system/activityConnTag/' + activityConnTagId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/activityGroup.js ================================================ import request from '@/utils/request' // 查询活动组队列表 export function listActivityGroup(query) { return request({ url: '/system/activityGroup/list', method: 'get', params: query }) } // 查询活动组队详细 export function getActivityGroup(groupId) { return request({ url: '/system/activityGroup/' + groupId, method: 'get' }) } // 新增活动组队 export function addActivityGroup(data) { return request({ url: '/system/activityGroup', method: 'post', data: data }) } // 修改活动组队 export function updateActivityGroup(data) { return request({ url: '/system/activityGroup', method: 'put', data: data }) } // 删除活动组队 export function delActivityGroup(groupId) { return request({ url: '/system/activityGroup/' + groupId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/activityGroupApply.js ================================================ import request from '@/utils/request' // 查询活动组队申请列列表 export function listActivityGroupApply(query) { return request({ url: '/system/activityGroupApply/list', method: 'get', params: query }) } // 查询活动组队申请列详细 export function getActivityGroupApply(applyId) { return request({ url: '/system/activityGroupApply/' + applyId, method: 'get' }) } // 新增活动组队申请列 export function addActivityGroupApply(data) { return request({ url: '/system/activityGroupApply', method: 'post', data: data }) } // 修改活动组队申请列 export function updateActivityGroupApply(data) { return request({ url: '/system/activityGroupApply', method: 'put', data: data }) } // 删除活动组队申请列 export function delActivityGroupApply(applyId) { return request({ url: '/system/activityGroupApply/' + applyId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/artist.js ================================================ import request from '@/utils/request' // 查询艺人列表 export function listArtist(query) { return request({ url: '/system/artist/list', method: 'get', params: query }) } // 查询艺人详细 export function getArtist(artistId) { return request({ url: '/system/artist/' + artistId, method: 'get' }) } // 新增艺人 export function addArtist(data) { return request({ url: '/system/artist', method: 'post', data: data }) } // 修改艺人 export function updateArtist(data) { return request({ url: '/system/artist', method: 'put', data: data }) } // 删除艺人 export function delArtist(artistId) { return request({ url: '/system/artist/' + artistId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/config.js ================================================ import request from '@/utils/request' // 查询参数列表 export function listConfig(query) { return request({ url: '/system/config/list', method: 'get', params: query }) } // 查询参数详细 export function getConfig(configId) { return request({ url: '/system/config/' + configId, method: 'get' }) } // 根据参数键名查询参数值 export function getConfigKey(configKey) { return request({ url: '/system/config/configKey/' + configKey, method: 'get' }) } // 新增参数配置 export function addConfig(data) { return request({ url: '/system/config', method: 'post', data: data }) } // 修改参数配置 export function updateConfig(data) { return request({ url: '/system/config', method: 'put', data: data }) } // 修改参数配置 export function updateConfigByKey(key, value) { return request({ url: '/system/config/updateByKey', method: 'put', data: { configKey: key, configValue: value } }) } // 删除参数配置 export function delConfig(configId) { return request({ url: '/system/config/' + configId, method: 'delete' }) } // 刷新参数缓存 export function refreshCache() { return request({ url: '/system/config/refreshCache', method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/dept.js ================================================ import request from '@/utils/request' // 查询部门列表 export function listDept(query) { return request({ url: '/system/dept/list', method: 'get', params: query }) } // 查询部门列表(排除节点) export function listDeptExcludeChild(deptId) { return request({ url: '/system/dept/list/exclude/' + deptId, method: 'get' }) } // 查询部门详细 export function getDept(deptId) { return request({ url: '/system/dept/' + deptId, method: 'get' }) } // 新增部门 export function addDept(data) { return request({ url: '/system/dept', method: 'post', data: data }) } // 修改部门 export function updateDept(data) { return request({ url: '/system/dept', method: 'put', data: data }) } // 删除部门 export function delDept(deptId) { return request({ url: '/system/dept/' + deptId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/dict/data.js ================================================ import request from '@/utils/request' // 查询字典数据列表 export function listData(query) { return request({ url: '/system/dict/data/list', method: 'get', params: query }) } // 查询字典数据详细 export function getData(dictCode) { return request({ url: '/system/dict/data/' + dictCode, method: 'get' }) } // 根据字典类型查询字典数据信息 export function getDicts(dictType) { return request({ url: '/system/dict/data/type/' + dictType, method: 'get' }) } // 新增字典数据 export function addData(data) { return request({ url: '/system/dict/data', method: 'post', data: data }) } // 修改字典数据 export function updateData(data) { return request({ url: '/system/dict/data', method: 'put', data: data }) } // 删除字典数据 export function delData(dictCode) { return request({ url: '/system/dict/data/' + dictCode, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/dict/type.js ================================================ import request from '@/utils/request' // 查询字典类型列表 export function listType(query) { return request({ url: '/system/dict/type/list', method: 'get', params: query }) } // 查询字典类型详细 export function getType(dictId) { return request({ url: '/system/dict/type/' + dictId, method: 'get' }) } // 新增字典类型 export function addType(data) { return request({ url: '/system/dict/type', method: 'post', data: data }) } // 修改字典类型 export function updateType(data) { return request({ url: '/system/dict/type', method: 'put', data: data }) } // 删除字典类型 export function delType(dictId) { return request({ url: '/system/dict/type/' + dictId, method: 'delete' }) } // 刷新字典缓存 export function refreshCache() { return request({ url: '/system/dict/type/refreshCache', method: 'delete' }) } // 获取字典选择框列表 export function optionselect() { return request({ url: '/system/dict/type/optionselect', method: 'get' }) } ================================================ FILE: ruoyi-ui/src/api/system/intro.js ================================================ import request from '@/utils/request' // 查询活动介绍列表 export function listIntro(query) { return request({ url: '/system/intro/list', method: 'get', params: query }) } // 查询活动介绍详细 export function getIntro(introId) { return request({ url: '/system/intro/' + introId, method: 'get' }) } // 新增活动介绍 export function addIntro(data) { return request({ url: '/system/intro', method: 'post', data: data }) } // 修改活动介绍 export function updateIntro(data) { return request({ url: '/system/intro', method: 'put', data: data }) } // 删除活动介绍 export function delIntro(introId) { return request({ url: '/system/intro/' + introId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/menu.js ================================================ import request from '@/utils/request' // 查询菜单列表 export function listMenu(query) { return request({ url: '/system/menu/list', method: 'get', params: query }) } // 查询菜单详细 export function getMenu(menuId) { return request({ url: '/system/menu/' + menuId, method: 'get' }) } // 查询菜单下拉树结构 export function treeselect() { return request({ url: '/system/menu/treeselect', method: 'get' }) } // 根据角色ID查询菜单下拉树结构 export function roleMenuTreeselect(roleId) { return request({ url: '/system/menu/roleMenuTreeselect/' + roleId, method: 'get' }) } // 新增菜单 export function addMenu(data) { return request({ url: '/system/menu', method: 'post', data: data }) } // 修改菜单 export function updateMenu(data) { return request({ url: '/system/menu', method: 'put', data: data }) } // 删除菜单 export function delMenu(menuId) { return request({ url: '/system/menu/' + menuId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/notice.js ================================================ import request from '@/utils/request' // 查询公告列表 export function listNotice(query) { return request({ url: '/system/notice/list', method: 'get', params: query }) } // 查询公告详细 export function getNotice(noticeId) { return request({ url: '/system/notice/' + noticeId, method: 'get' }) } // 新增公告 export function addNotice(data) { return request({ url: '/system/notice', method: 'post', data: data }) } // 修改公告 export function updateNotice(data) { return request({ url: '/system/notice', method: 'put', data: data }) } // 删除公告 export function delNotice(noticeId) { return request({ url: '/system/notice/' + noticeId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/official.js ================================================ import request from '@/utils/request' // 查询官方消息列表 export function listOfficial(query) { return request({ url: '/system/official/list', method: 'get', params: query }) } // 查询官方消息详细 export function getOfficial(officialId) { return request({ url: '/system/official/' + officialId, method: 'get' }) } // 新增官方消息 export function addOfficial(data) { return request({ url: '/system/official', method: 'post', data: data }) } // 修改官方消息 export function updateOfficial(data) { return request({ url: '/system/official', method: 'put', data: data }) } // 删除官方消息 export function delOfficial(officialId) { return request({ url: '/system/official/' + officialId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/organizer.js ================================================ import request from '@/utils/request' // 查询活动主办方列表 export function listOrganizer(query) { return request({ url: '/system/organizer/list', method: 'get', params: query }) } // 查询活动主办方详细 export function getOrganizer(organizerId) { return request({ url: '/system/organizer/' + organizerId, method: 'get' }) } // 新增活动主办方 export function addOrganizer(data) { return request({ url: '/system/organizer', method: 'post', data: data }) } // 修改活动主办方 export function updateOrganizer(data) { return request({ url: '/system/organizer', method: 'put', data: data }) } // 删除活动主办方 export function delOrganizer(organizerId) { return request({ url: '/system/organizer/' + organizerId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/organizerTicket.js ================================================ import request from '@/utils/request' // 查询主办方票务列表 export function listOrganizerTicket(query) { return request({ url: '/system/organizerTicket/list', method: 'get', params: query }) } // 查询主办方票务详细 export function getOrganizerTicket(organizerTicketId) { return request({ url: '/system/organizerTicket/' + organizerTicketId, method: 'get' }) } // 新增主办方票务 export function addOrganizerTicket(data) { return request({ url: '/system/organizerTicket', method: 'post', data: data }) } // 修改主办方票务 export function updateOrganizerTicket(data) { return request({ url: '/system/organizerTicket', method: 'put', data: data }) } // 删除主办方票务 export function delOrganizerTicket(organizerTicketId) { return request({ url: '/system/organizerTicket/' + organizerTicketId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/oss.js ================================================ import request from '@/utils/request' // 查询OSS对象存储列表 export function listOss(query) { return request({ url: '/system/oss/list', method: 'get', params: query }) } // 查询OSS对象基于id串 export function listByIds(ossId) { return request({ url: '/system/oss/listByIds/' + ossId, method: 'get' }) } // 删除OSS对象存储 export function delOss(ossId) { return request({ url: '/system/oss/' + ossId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/ossConfig.js ================================================ import request from '@/utils/request' // 查询对象存储配置列表 export function listOssConfig(query) { return request({ url: '/system/oss/config/list', method: 'get', params: query }) } // 查询对象存储配置详细 export function getOssConfig(ossConfigId) { return request({ url: '/system/oss/config/' + ossConfigId, method: 'get' }) } // 新增对象存储配置 export function addOssConfig(data) { return request({ url: '/system/oss/config', method: 'post', data: data }) } // 修改对象存储配置 export function updateOssConfig(data) { return request({ url: '/system/oss/config', method: 'put', data: data }) } // 删除对象存储配置 export function delOssConfig(ossConfigId) { return request({ url: '/system/oss/config/' + ossConfigId, method: 'delete' }) } // 对象存储状态修改 export function changeOssConfigStatus(ossConfigId, status, configKey) { const data = { ossConfigId, status, configKey } return request({ url: '/system/oss/config/changeStatus', method: 'put', data: data }) } ================================================ FILE: ruoyi-ui/src/api/system/post.js ================================================ import request from '@/utils/request' // 查询岗位列表 export function listPost(query) { return request({ url: '/system/post/list', method: 'get', params: query }) } // 查询岗位详细 export function getPost(postId) { return request({ url: '/system/post/' + postId, method: 'get' }) } // 新增岗位 export function addPost(data) { return request({ url: '/system/post', method: 'post', data: data }) } // 修改岗位 export function updatePost(data) { return request({ url: '/system/post', method: 'put', data: data }) } // 删除岗位 export function delPost(postId) { return request({ url: '/system/post/' + postId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/pzc_order.js ================================================ import request from '@/utils/request' // 查询订单列表 export function listPzc_order(query) { return request({ url: '/system/pzc_order/list', method: 'get', params: query }) } // 查询订单详细 export function getPzc_order(orderId) { return request({ url: '/system/pzc_order/' + orderId, method: 'get' }) } // 新增订单 export function addPzc_order(data) { return request({ url: '/system/pzc_order', method: 'post', data: data }) } // 修改订单 export function updatePzc_order(data) { return request({ url: '/system/pzc_order', method: 'put', data: data }) } // 删除订单 export function delPzc_order(orderId) { return request({ url: '/system/pzc_order/' + orderId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/pzc_user.js ================================================ import request from '@/utils/request' // 查询用户列表 export function listPzc_user(query) { return request({ url: '/system/pzc_user/list', method: 'get', params: query }) } //更新用户余额 export function update_money(data) { return request({ url: '/system/pzc_user/updateMoney', method: 'post', data: data }) } // 查询用户详细 export function getPzc_user(userId) { return request({ url: '/system/pzc_user/' + userId, method: 'get' }) } // 新增用户 export function addPzc_user(data) { return request({ url: '/system/pzc_user', method: 'post', data: data }) } // 修改用户 export function updatePzc_user(data) { return request({ url: '/system/pzc_user', method: 'put', data: data }) } // 删除用户 export function delPzc_user(userId) { return request({ url: '/system/pzc_user/' + userId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/region.js ================================================ import request from '@/utils/request' // 查询地区列表 export function listRegion(query) { return request({ url: '/system/region/list', method: 'get', params: query }) } // 查询地区详细 export function getRegion(regionId) { return request({ url: '/system/region/' + regionId, method: 'get' }) } // 新增地区 export function addRegion(data) { return request({ url: '/system/region', method: 'post', data: data }) } // 修改地区 export function updateRegion(data) { return request({ url: '/system/region', method: 'put', data: data }) } // 删除地区 export function delRegion(regionId) { return request({ url: '/system/region/' + regionId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/role.js ================================================ import request from '@/utils/request' // 查询角色列表 export function listRole(query) { return request({ url: '/system/role/list', method: 'get', params: query }) } // 查询角色详细 export function getRole(roleId) { return request({ url: '/system/role/' + roleId, method: 'get' }) } // 新增角色 export function addRole(data) { return request({ url: '/system/role', method: 'post', data: data }) } // 修改角色 export function updateRole(data) { return request({ url: '/system/role', method: 'put', data: data }) } // 角色数据权限 export function dataScope(data) { return request({ url: '/system/role/dataScope', method: 'put', data: data }) } // 角色状态修改 export function changeRoleStatus(roleId, status) { const data = { roleId, status } return request({ url: '/system/role/changeStatus', method: 'put', data: data }) } // 删除角色 export function delRole(roleId) { return request({ url: '/system/role/' + roleId, method: 'delete' }) } // 查询角色已授权用户列表 export function allocatedUserList(query) { return request({ url: '/system/role/authUser/allocatedList', method: 'get', params: query }) } // 查询角色未授权用户列表 export function unallocatedUserList(query) { return request({ url: '/system/role/authUser/unallocatedList', method: 'get', params: query }) } // 取消用户授权角色 export function authUserCancel(data) { return request({ url: '/system/role/authUser/cancel', method: 'put', data: data }) } // 批量取消用户授权角色 export function authUserCancelAll(data) { return request({ url: '/system/role/authUser/cancelAll', method: 'put', params: data }) } // 授权用户选择 export function authUserSelectAll(data) { return request({ url: '/system/role/authUser/selectAll', method: 'put', params: data }) } // 根据角色ID查询部门树结构 export function deptTreeSelect(roleId) { return request({ url: '/system/role/deptTree/' + roleId, method: 'get' }) } ================================================ FILE: ruoyi-ui/src/api/system/tag.js ================================================ import request from '@/utils/request' // 查询活动标签列表 export function listTag(query) { return request({ url: '/system/tag/list', method: 'get', params: query }) } // 查询活动标签详细 export function getTag(tagId) { return request({ url: '/system/tag/' + tagId, method: 'get' }) } // 新增活动标签 export function addTag(data) { return request({ url: '/system/tag', method: 'post', data: data }) } // 修改活动标签 export function updateTag(data) { return request({ url: '/system/tag', method: 'put', data: data }) } // 删除活动标签 export function delTag(tagId) { return request({ url: '/system/tag/' + tagId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/user.js ================================================ import request from '@/utils/request' import { parseStrEmpty } from "@/utils/ruoyi"; // 查询用户列表 export function listUser(query) { return request({ url: '/system/user/list', method: 'get', params: query }) } // 查询用户详细 export function getUser(userId) { return request({ url: '/system/user/' + parseStrEmpty(userId), method: 'get' }) } // 新增用户 export function addUser(data) { return request({ url: '/system/user', method: 'post', data: data }) } // 修改用户 export function updateUser(data) { return request({ url: '/system/user', method: 'put', data: data }) } // 删除用户 export function delUser(userId) { return request({ url: '/system/user/' + userId, method: 'delete' }) } // 用户密码重置 export function resetUserPwd(userId, password) { const data = { userId, password } return request({ url: '/system/user/resetPwd', method: 'put', data: data }) } // 用户状态修改 export function changeUserStatus(userId, status) { const data = { userId, status } return request({ url: '/system/user/changeStatus', method: 'put', data: data }) } // 查询用户个人信息 export function getUserProfile() { return request({ url: '/system/user/profile', method: 'get' }) } // 修改用户个人信息 export function updateUserProfile(data) { return request({ url: '/system/user/profile', method: 'put', data: data }) } // 用户密码重置 export function updateUserPwd(oldPassword, newPassword) { const data = { oldPassword, newPassword } return request({ url: '/system/user/profile/updatePwd', method: 'put', params: data }) } // 用户头像上传 export function uploadAvatar(data) { return request({ url: '/system/user/profile/avatar', method: 'post', data: data }) } // 查询授权角色 export function getAuthRole(userId) { return request({ url: '/system/user/authRole/' + userId, method: 'get' }) } // 保存授权角色 export function updateAuthRole(data) { return request({ url: '/system/user/authRole', method: 'put', params: data }) } // 查询部门下拉树结构 export function deptTreeSelect() { return request({ url: '/system/user/deptTree', method: 'get' }) } ================================================ FILE: ruoyi-ui/src/api/system/userCollect.js ================================================ import request from '@/utils/request' // 查询用户收藏活动列表 export function listUserCollect(query) { return request({ url: '/system/userCollect/list', method: 'get', params: query }) } // 查询用户收藏活动详细 export function getUserCollect(collectId) { return request({ url: '/system/userCollect/' + collectId, method: 'get' }) } // 新增用户收藏活动 export function addUserCollect(data) { return request({ url: '/system/userCollect', method: 'post', data: data }) } // 修改用户收藏活动 export function updateUserCollect(data) { return request({ url: '/system/userCollect', method: 'put', data: data }) } // 删除用户收藏活动 export function delUserCollect(collectId) { return request({ url: '/system/userCollect/' + collectId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/userHistory.js ================================================ import request from '@/utils/request' // 查询用户操作历史记录列表 export function listUserHistory(query) { return request({ url: '/system/userHistory/list', method: 'get', params: query }) } // 查询用户操作历史记录详细 export function getUserHistory(historyId) { return request({ url: '/system/userHistory/' + historyId, method: 'get' }) } // 新增用户操作历史记录 export function addUserHistory(data) { return request({ url: '/system/userHistory', method: 'post', data: data }) } // 修改用户操作历史记录 export function updateUserHistory(data) { return request({ url: '/system/userHistory', method: 'put', data: data }) } // 删除用户操作历史记录 export function delUserHistory(historyId) { return request({ url: '/system/userHistory/' + historyId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/userPhoto.js ================================================ import request from '@/utils/request' // 查询用户资料相册列表 export function listUserPhoto(query) { return request({ url: '/system/userPhoto/list', method: 'get', params: query }) } // 查询用户资料相册详细 export function getUserPhoto(photoId) { return request({ url: '/system/userPhoto/' + photoId, method: 'get' }) } // 新增用户资料相册 export function addUserPhoto(data) { return request({ url: '/system/userPhoto', method: 'post', data: data }) } // 修改用户资料相册 export function updateUserPhoto(data) { return request({ url: '/system/userPhoto', method: 'put', data: data }) } // 删除用户资料相册 export function delUserPhoto(photoId) { return request({ url: '/system/userPhoto/' + photoId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/userTalk.js ================================================ import request from '@/utils/request' // 查询用户聊天列表 export function listUserTalk(query) { return request({ url: '/system/userTalk/list', method: 'get', params: query }) } // 查询用户聊天详细 export function getUserTalk(talkId) { return request({ url: '/system/userTalk/' + talkId, method: 'get' }) } // 新增用户聊天 export function addUserTalk(data) { return request({ url: '/system/userTalk', method: 'post', data: data }) } // 修改用户聊天 export function updateUserTalk(data) { return request({ url: '/system/userTalk', method: 'put', data: data }) } // 删除用户聊天 export function delUserTalk(talkId) { return request({ url: '/system/userTalk/' + talkId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/system/viewPager.js ================================================ import request from '@/utils/request' // 查询轮播图列表 export function listViewPager(query) { return request({ url: '/system/viewPager/list', method: 'get', params: query }) } // 查询轮播图详细 export function getViewPager(viewPagerId) { return request({ url: '/system/viewPager/' + viewPagerId, method: 'get' }) } // 新增轮播图 export function addViewPager(data) { return request({ url: '/system/viewPager', method: 'post', data: data }) } // 修改轮播图 export function updateViewPager(data) { return request({ url: '/system/viewPager', method: 'put', data: data }) } // 删除轮播图 export function delViewPager(viewPagerId) { return request({ url: '/system/viewPager/' + viewPagerId, method: 'delete' }) } ================================================ FILE: ruoyi-ui/src/api/tool/gen.js ================================================ import request from '@/utils/request' // 查询生成表数据 export function listTable(query) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/list', method: 'get', params: query }) } // 查询db数据库列表 export function listDbTable(query) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/db/list', method: 'get', params: query }) } // 查询表详细信息 export function getGenTable(tableId) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/' + tableId, method: 'get' }) } // 修改代码生成信息 export function updateGenTable(data) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen', method: 'put', data: data }) } // 导入表 export function importTable(data) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/importTable', method: 'post', params: data }) } // 预览生成代码 export function previewTable(tableId) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/preview/' + tableId, method: 'get' }) } // 删除表数据 export function delTable(tableId) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/' + tableId, method: 'delete' }) } // 生成代码(自定义路径) export function genCode(tableName) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/genCode/' + tableName, method: 'get' }) } // 同步数据库 export function synchDb(tableName) { return request({ headers: { 'datasource': localStorage.getItem("dataName") }, url: '/tool/gen/synchDb/' + tableName, method: 'get' }) } ================================================ FILE: ruoyi-ui/src/assets/icons/index.js ================================================ import Vue from 'vue' import SvgIcon from '@/components/SvgIcon'// svg component // register globally Vue.component('svg-icon', SvgIcon) const req = require.context('./svg', false, /\.svg$/) const requireAll = requireContext => requireContext.keys().map(requireContext) requireAll(req) ================================================ FILE: ruoyi-ui/src/assets/icons/svgo.yml ================================================ # replace default config # multipass: true # full: true plugins: # - name # # or: # - name: false # - name: true # # or: # - name: # param1: 1 # param2: 2 - removeAttrs: attrs: - 'fill' - 'fill-rule' ================================================ FILE: ruoyi-ui/src/assets/styles/btn.scss ================================================ @import './variables.scss'; @mixin colorBtn($color) { background: $color; &:hover { color: $color; &:before, &:after { background: $color; } } } .blue-btn { @include colorBtn($blue) } .light-blue-btn { @include colorBtn($light-blue) } .red-btn { @include colorBtn($red) } .pink-btn { @include colorBtn($pink) } .green-btn { @include colorBtn($green) } .tiffany-btn { @include colorBtn($tiffany) } .yellow-btn { @include colorBtn($yellow) } .pan-btn { font-size: 14px; color: #fff; padding: 14px 36px; border-radius: 8px; border: none; outline: none; transition: 600ms ease all; position: relative; display: inline-block; &:hover { background: #fff; &:before, &:after { width: 100%; transition: 600ms ease all; } } &:before, &:after { content: ''; position: absolute; top: 0; right: 0; height: 2px; width: 0; transition: 400ms ease all; } &::after { right: inherit; top: inherit; left: 0; bottom: 0; } } .custom-button { display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; background: #fff; color: #fff; -webkit-appearance: none; text-align: center; box-sizing: border-box; outline: 0; margin: 0; padding: 10px 15px; font-size: 14px; border-radius: 4px; } ================================================ FILE: ruoyi-ui/src/assets/styles/element-ui.scss ================================================ // cover some element-ui styles .el-breadcrumb__inner, .el-breadcrumb__inner a { font-weight: 400 !important; } .el-upload { input[type="file"] { display: none !important; } } .el-upload__input { display: none; } .cell { .el-tag { margin-right: 0px; } } .small-padding { .cell { padding-left: 5px; padding-right: 5px; } } .fixed-width { .el-button--mini { padding: 7px 10px; width: 60px; } } .status-col { .cell { padding: 0 10px; text-align: center; .el-tag { margin-right: 0px; } } } // to fixed https://github.com/ElemeFE/element/issues/2461 .el-dialog { transform: none; left: 0; position: relative; margin: 0 auto; } // refine element ui upload .upload-container { .el-upload { width: 100%; .el-upload-dragger { width: 100%; height: 200px; } } } // dropdown .el-dropdown-menu { a { display: block } } // fix date-picker ui bug in filter-item .el-range-editor.el-input__inner { display: inline-flex !important; } // to fix el-date-picker css style .el-range-separator { box-sizing: content-box; } .el-menu--collapse > div > .el-submenu > .el-submenu__title .el-submenu__icon-arrow { display: none; } ================================================ FILE: ruoyi-ui/src/assets/styles/element-variables.scss ================================================ /** * I think element-ui's default theme color is too light for long-term use. * So I modified the default color and you can modify it to your liking. **/ /* theme color */ $--color-primary: #1890ff; $--color-success: #13ce66; $--color-warning: #ffba00; $--color-danger: #ff4949; // $--color-info: #1E1E1E; $--button-font-weight: 400; // $--color-text-regular: #1f2d3d; $--border-color-light: #dfe4ed; $--border-color-lighter: #e6ebf5; $--table-border: 1px solid #dfe6ec; /* icon font path, required */ $--font-path: '~element-ui/lib/theme-chalk/fonts'; @import "~element-ui/packages/theme-chalk/src/index"; // the :export directive is the magic sauce for webpack // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass :export { theme: $--color-primary; } ================================================ FILE: ruoyi-ui/src/assets/styles/index.scss ================================================ @import './variables.scss'; @import './mixin.scss'; @import './transition.scss'; @import './element-ui.scss'; @import './sidebar.scss'; @import './btn.scss'; body { height: 100%; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; } label { font-weight: 700; } html { height: 100%; box-sizing: border-box; } #app { height: 100%; } *, *:before, *:after { box-sizing: inherit; } .no-padding { padding: 0px !important; } .padding-content { padding: 4px 0; } a:focus, a:active { outline: none; } a, a:focus, a:hover { cursor: pointer; color: inherit; text-decoration: none; } div:focus { outline: none; } .fr { float: right; } .fl { float: left; } .pr-5 { padding-right: 5px; } .pl-5 { padding-left: 5px; } .block { display: block; } .pointer { cursor: pointer; } .inlineBlock { display: block; } .clearfix { &:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; } } aside { background: #eef1f6; padding: 8px 24px; margin-bottom: 20px; border-radius: 2px; display: block; line-height: 32px; font-size: 16px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; color: #2c3e50; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; a { color: #337ab7; cursor: pointer; &:hover { color: rgb(32, 160, 255); } } } //main-container全局样式 .app-container { padding: 20px; } .components-container { margin: 30px 50px; position: relative; } .pagination-container { margin-top: 30px; } .text-center { text-align: center } .sub-navbar { height: 50px; line-height: 50px; position: relative; width: 100%; text-align: right; padding-right: 20px; transition: 600ms ease position; background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); .subtitle { font-size: 20px; color: #fff; } &.draft { background: #d0d0d0; } &.deleted { background: #d0d0d0; } } .link-type, .link-type:focus { color: #337ab7; cursor: pointer; &:hover { color: rgb(32, 160, 255); } } .filter-container { padding-bottom: 10px; .filter-item { display: inline-block; vertical-align: middle; margin-bottom: 10px; } } ================================================ FILE: ruoyi-ui/src/assets/styles/mixin.scss ================================================ @mixin clearfix { &:after { content: ""; display: table; clear: both; } } @mixin scrollBar { &::-webkit-scrollbar-track-piece { background: #d3dce6; } &::-webkit-scrollbar { width: 6px; } &::-webkit-scrollbar-thumb { background: #99a9bf; border-radius: 20px; } } @mixin relative { position: relative; width: 100%; height: 100%; } @mixin pct($pct) { width: #{$pct}; position: relative; margin: 0 auto; } @mixin triangle($width, $height, $color, $direction) { $width: $width/2; $color-border-style: $height solid $color; $transparent-border-style: $width solid transparent; height: 0; width: 0; @if $direction==up { border-bottom: $color-border-style; border-left: $transparent-border-style; border-right: $transparent-border-style; } @else if $direction==right { border-left: $color-border-style; border-top: $transparent-border-style; border-bottom: $transparent-border-style; } @else if $direction==down { border-top: $color-border-style; border-left: $transparent-border-style; border-right: $transparent-border-style; } @else if $direction==left { border-right: $color-border-style; border-top: $transparent-border-style; border-bottom: $transparent-border-style; } } ================================================ FILE: ruoyi-ui/src/assets/styles/ruoyi.scss ================================================ /** * 通用css样式布局处理 * Copyright (c) 2019 ruoyi */ /** 基础通用 **/ .pt5 { padding-top: 5px; } .pr5 { padding-right: 5px; } .pb5 { padding-bottom: 5px; } .mt5 { margin-top: 5px; } .mr5 { margin-right: 5px; } .mb5 { margin-bottom: 5px; } .mb8 { margin-bottom: 8px; } .ml5 { margin-left: 5px; } .mt10 { margin-top: 10px; } .mr10 { margin-right: 10px; } .mb10 { margin-bottom: 10px; } .ml10 { margin-left: 10px; } .mt20 { margin-top: 20px; } .mr20 { margin-right: 20px; } .mb20 { margin-bottom: 20px; } .ml20 { margin-left: 20px; } .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: inherit; } .el-message-box__status + .el-message-box__message{ word-break: break-word; } .el-dialog:not(.is-fullscreen) { margin-top: 6vh !important; } .el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body { overflow: auto; overflow-x: hidden; max-height: 70vh; padding: 10px 20px 0; } .el-table { .el-table__header-wrapper, .el-table__fixed-header-wrapper { th { word-break: break-word; background-color: #f8f8f9; color: #515a6e; height: 40px; font-size: 13px; } } .el-table__body-wrapper { .el-button [class*="el-icon-"] + span { margin-left: 1px; } } } /** 表单布局 **/ .form-header { font-size: 15px; color: #6379bb; border-bottom: 1px solid #ddd; margin: 8px 10px 25px 10px; padding-bottom: 5px } /** 表格布局 **/ .pagination-container { position: relative; height: 25px; margin-bottom: 10px; margin-top: 15px; padding: 10px 20px !important; } /* tree border */ .tree-border { margin-top: 5px; border: 1px solid #e5e6e7; background: #FFFFFF none; border-radius: 4px; } .pagination-container .el-pagination { right: 0; position: absolute; } @media (max-width: 768px) { .pagination-container .el-pagination > .el-pagination__jump { display: none !important; } .pagination-container .el-pagination > .el-pagination__sizes { display: none !important; } } .el-table .fixed-width .el-button--mini { padding-left: 0; padding-right: 0; width: inherit; } /** 表格更多操作下拉样式 */ .el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine { cursor: pointer; margin-left: 5px; } .el-table .el-dropdown, .el-icon-arrow-down { font-size: 12px; } .el-tree-node__content > .el-checkbox { margin-right: 8px; } .list-group-striped > .list-group-item { border-left: 0; border-right: 0; border-radius: 0; padding-left: 0; padding-right: 0; } .list-group { padding-left: 0px; list-style: none; } .list-group-item { border-bottom: 1px solid #e7eaec; border-top: 1px solid #e7eaec; margin-bottom: -1px; padding: 11px 0px; font-size: 13px; } .pull-right { float: right !important; } .el-card__header { padding: 14px 15px 7px; min-height: 40px; } .el-card__body { padding: 15px 20px 20px 20px; } .card-box { padding-right: 15px; padding-left: 15px; margin-bottom: 10px; } /* button color */ .el-button--cyan.is-active, .el-button--cyan:active { background: #20B2AA; border-color: #20B2AA; color: #FFFFFF; } .el-button--cyan:focus, .el-button--cyan:hover { background: #48D1CC; border-color: #48D1CC; color: #FFFFFF; } .el-button--cyan { background-color: #20B2AA; border-color: #20B2AA; color: #FFFFFF; } /* text color */ .text-navy { color: #1ab394; } .text-primary { color: inherit; } .text-success { color: #1c84c6; } .text-info { color: #23c6c8; } .text-warning { color: #f8ac59; } .text-danger { color: #ed5565; } .text-muted { color: #888888; } /* image */ .img-circle { border-radius: 50%; } .img-lg { width: 120px; height: 120px; } .avatar-upload-preview { position: relative; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 200px; height: 200px; border-radius: 50%; box-shadow: 0 0 4px #ccc; overflow: hidden; } /* 拖拽列样式 */ .sortable-ghost { opacity: .8; color: #fff !important; background: #42b983 !important; } .top-right-btn { position: relative; float: right; } ================================================ FILE: ruoyi-ui/src/assets/styles/sidebar.scss ================================================ #app { .main-container { height: 100%; transition: margin-left .28s; margin-left: $base-sidebar-width; position: relative; } .sidebarHide { margin-left: 0!important; } .sidebar-container { -webkit-transition: width .28s; transition: width 0.28s; width: $base-sidebar-width !important; background-color: $base-menu-background; height: 100%; position: fixed; font-size: 0px; top: 0; bottom: 0; left: 0; z-index: 1001; overflow: hidden; -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35); box-shadow: 2px 0 6px rgba(0,21,41,.35); // reset element-ui css .horizontal-collapse-transition { transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; } .scrollbar-wrapper { overflow-x: hidden !important; } .el-scrollbar__bar.is-vertical { right: 0px; } .el-scrollbar { height: 100%; } &.has-logo { .el-scrollbar { height: calc(100% - 50px); } } .is-horizontal { display: none; } a { display: inline-block; width: 100%; overflow: hidden; } .svg-icon { margin-right: 16px; } .el-menu { border: none; height: 100%; width: 100% !important; } .el-menu-item, .el-submenu__title { overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; } // menu hover .submenu-title-noDropdown, .el-submenu__title { &:hover { background-color: rgba(0, 0, 0, 0.06) !important; } } & .theme-dark .is-active > .el-submenu__title { color: $base-menu-color-active !important; } & .nest-menu .el-submenu>.el-submenu__title, & .el-submenu .el-menu-item { min-width: $base-sidebar-width !important; &:hover { background-color: rgba(0, 0, 0, 0.06) !important; } } & .theme-dark .nest-menu .el-submenu>.el-submenu__title, & .theme-dark .el-submenu .el-menu-item { background-color: $base-sub-menu-background !important; &:hover { background-color: $base-sub-menu-hover !important; } } } .hideSidebar { .sidebar-container { width: 54px !important; } .main-container { margin-left: 54px; } .submenu-title-noDropdown { padding: 0 !important; position: relative; .el-tooltip { padding: 0 !important; .svg-icon { margin-left: 20px; } } } .el-submenu { overflow: hidden; &>.el-submenu__title { padding: 0 !important; .svg-icon { margin-left: 20px; } } } .el-menu--collapse { .el-submenu { &>.el-submenu__title { &>span { height: 0; width: 0; overflow: hidden; visibility: hidden; display: inline-block; } } } } } .el-menu--collapse .el-menu .el-submenu { min-width: $base-sidebar-width !important; } // mobile responsive .mobile { .main-container { margin-left: 0px; } .sidebar-container { transition: transform .28s; width: $base-sidebar-width !important; } &.hideSidebar { .sidebar-container { pointer-events: none; transition-duration: 0.3s; transform: translate3d(-$base-sidebar-width, 0, 0); } } } .withoutAnimation { .main-container, .sidebar-container { transition: none; } } } // when menu collapsed .el-menu--vertical { &>.el-menu { .svg-icon { margin-right: 16px; } } .nest-menu .el-submenu>.el-submenu__title, .el-menu-item { &:hover { // you can use $subMenuHover background-color: rgba(0, 0, 0, 0.06) !important; } } // the scroll bar appears when the subMenu is too long >.el-menu--popup { max-height: 100vh; overflow-y: auto; &::-webkit-scrollbar-track-piece { background: #d3dce6; } &::-webkit-scrollbar { width: 6px; } &::-webkit-scrollbar-thumb { background: #99a9bf; border-radius: 20px; } } } ================================================ FILE: ruoyi-ui/src/assets/styles/transition.scss ================================================ // global transition css /* fade */ .fade-enter-active, .fade-leave-active { transition: opacity 0.28s; } .fade-enter, .fade-leave-active { opacity: 0; } /* fade-transform */ .fade-transform--move, .fade-transform-leave-active, .fade-transform-enter-active { transition: all .5s; } .fade-transform-enter { opacity: 0; transform: translateX(-30px); } .fade-transform-leave-to { opacity: 0; transform: translateX(30px); } /* breadcrumb transition */ .breadcrumb-enter-active, .breadcrumb-leave-active { transition: all .5s; } .breadcrumb-enter, .breadcrumb-leave-active { opacity: 0; transform: translateX(20px); } .breadcrumb-move { transition: all .5s; } .breadcrumb-leave-active { position: absolute; } ================================================ FILE: ruoyi-ui/src/assets/styles/variables.scss ================================================ // base color $blue:#324157; $light-blue:#3A71A8; $red:#C03639; $pink: #E65D6E; $green: #30B08F; $tiffany: #4AB7BD; $yellow:#FEC171; $panGreen: #30B08F; // 默认菜单主题风格 $base-menu-color:#bfcbd9; $base-menu-color-active:#f4f4f5; $base-menu-background:#304156; $base-logo-title-color: #ffffff; $base-menu-light-color:rgba(0,0,0,.70); $base-menu-light-background:#ffffff; $base-logo-light-title-color: #001529; $base-sub-menu-background:#1f2d3d; $base-sub-menu-hover:#001528; // 自定义暗色菜单风格 /** $base-menu-color:hsla(0,0%,100%,.65); $base-menu-color-active:#fff; $base-menu-background:#001529; $base-logo-title-color: #ffffff; $base-menu-light-color:rgba(0,0,0,.70); $base-menu-light-background:#ffffff; $base-logo-light-title-color: #001529; $base-sub-menu-background:#000c17; $base-sub-menu-hover:#001528; */ $base-sidebar-width: 200px; // the :export directive is the magic sauce for webpack // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass :export { menuColor: $base-menu-color; menuLightColor: $base-menu-light-color; menuColorActive: $base-menu-color-active; menuBackground: $base-menu-background; menuLightBackground: $base-menu-light-background; subMenuBackground: $base-sub-menu-background; subMenuHover: $base-sub-menu-hover; sideBarWidth: $base-sidebar-width; logoTitleColor: $base-logo-title-color; logoLightTitleColor: $base-logo-light-title-color } ================================================ FILE: ruoyi-ui/src/components/Breadcrumb/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/day.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/hour.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/min.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/month.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/result.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/second.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/week.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Crontab/year.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/DictData/index.js ================================================ import Vue from 'vue' import store from '@/store' import DataDict from '@/utils/dict' import { getDicts as getDicts } from '@/api/system/dict/data' function searchDictByKey(dict, key) { if (key == null && key == "") { return null } try { for (let i = 0; i < dict.length; i++) { if (dict[i].key == key) { return dict[i].value } } } catch (e) { return null } } function install() { Vue.use(DataDict, { metas: { '*': { labelField: 'dictLabel', valueField: 'dictValue', request(dictMeta) { const storeDict = searchDictByKey(store.getters.dict, dictMeta.type) if (storeDict) { return new Promise(resolve => { resolve(storeDict) }) } else { return new Promise((resolve, reject) => { getDicts(dictMeta.type).then(res => { store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data }) resolve(res.data) }).catch(error => { reject(error) }) }) } }, }, }, }) } export default { install, } ================================================ FILE: ruoyi-ui/src/components/DictTag/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Editor/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/FileUpload/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Hamburger/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/HeaderSearch/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/IconSelect/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/IconSelect/requireIcons.js ================================================ const req = require.context('../../assets/icons/svg', false, /\.svg$/) const requireAll = requireContext => requireContext.keys() const re = /\.\/(.*)\.svg/ const icons = requireAll(req).map(i => { return i.match(re)[1] }) export default icons ================================================ FILE: ruoyi-ui/src/components/ImagePreview/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/ImageUpload/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Pagination/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/PanThumb/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/ParentView/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/RightPanel/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/RightToolbar/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/RuoYi/Doc/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/RuoYi/Git/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/Screenfull/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/SizeSelect/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/SvgIcon/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/ThemePicker/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/TopNav/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/components/iFrame/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/Navbar.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/Settings/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js ================================================ export default { computed: { device() { return this.$store.state.app.device } }, mounted() { // In order to fix the click on menu on the ios device will trigger the mouseleave bug this.fixBugIniOS() }, methods: { fixBugIniOS() { const $subMenu = this.$refs.subMenu if ($subMenu) { const handleMouseleave = $subMenu.handleMouseleave $subMenu.handleMouseleave = (e) => { if (this.device === 'mobile') { return } handleMouseleave(e) } } } } } ================================================ FILE: ruoyi-ui/src/layout/components/Sidebar/Item.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/Sidebar/Link.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/Sidebar/Logo.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/Sidebar/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/TagsView/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/components/index.js ================================================ export { default as AppMain } from './AppMain' export { default as Navbar } from './Navbar' export { default as Settings } from './Settings' export { default as Sidebar } from './Sidebar/index.vue' export { default as TagsView } from './TagsView/index.vue' ================================================ FILE: ruoyi-ui/src/layout/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/layout/mixin/ResizeHandler.js ================================================ import store from '@/store' const { body } = document const WIDTH = 992 // refer to Bootstrap's responsive design export default { watch: { $route(route) { if (this.device === 'mobile' && this.sidebar.opened) { store.dispatch('app/closeSideBar', { withoutAnimation: false }) } } }, beforeMount() { window.addEventListener('resize', this.$_resizeHandler) }, beforeDestroy() { window.removeEventListener('resize', this.$_resizeHandler) }, mounted() { const isMobile = this.$_isMobile() if (isMobile) { store.dispatch('app/toggleDevice', 'mobile') store.dispatch('app/closeSideBar', { withoutAnimation: true }) } }, methods: { // use $_ for mixins properties // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential $_isMobile() { const rect = body.getBoundingClientRect() return rect.width - 1 < WIDTH }, $_resizeHandler() { if (!document.hidden) { const isMobile = this.$_isMobile() store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') if (isMobile) { store.dispatch('app/closeSideBar', { withoutAnimation: true }) } } } } } ================================================ FILE: ruoyi-ui/src/main.js ================================================ import Vue from 'vue' import Cookies from 'js-cookie' import Element from 'element-ui' import './assets/styles/element-variables.scss' import '@/assets/styles/index.scss' // global css import '@/assets/styles/ruoyi.scss' // ruoyi css import App from './App' import store from './store' import router from './router' import directive from './directive' // directive import plugins from './plugins' // plugins import { download } from '@/utils/request' import './assets/icons' // icon import './permission' // permission control import { getDicts } from "@/api/system/dict/data"; import { getConfigKey, updateConfigByKey } from "@/api/system/config"; import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"; // 分页组件 import Pagination from "@/components/Pagination"; // 自定义表格工具组件 import RightToolbar from "@/components/RightToolbar" // 富文本组件 import Editor from "@/components/Editor" // 文件上传组件 import FileUpload from "@/components/FileUpload" // 图片上传组件 import ImageUpload from "@/components/ImageUpload" // 图片预览组件 import ImagePreview from "@/components/ImagePreview" // 字典标签组件 import DictTag from '@/components/DictTag' // 头部标签组件 import VueMeta from 'vue-meta' // 字典数据组件 import DictData from '@/components/DictData' // 高德地图组件 import VueAMap from 'vue-amap'; // 全局方法挂载 Vue.prototype.getDicts = getDicts Vue.prototype.getConfigKey = getConfigKey Vue.prototype.updateConfigByKey = updateConfigByKey Vue.prototype.parseTime = parseTime Vue.prototype.resetForm = resetForm Vue.prototype.addDateRange = addDateRange Vue.prototype.selectDictLabel = selectDictLabel Vue.prototype.selectDictLabels = selectDictLabels Vue.prototype.download = download Vue.prototype.handleTree = handleTree // 全局组件挂载 Vue.component('DictTag', DictTag) Vue.component('Pagination', Pagination) Vue.component('RightToolbar', RightToolbar) Vue.component('Editor', Editor) Vue.component('FileUpload', FileUpload) Vue.component('ImageUpload', ImageUpload) Vue.component('ImagePreview', ImagePreview) Vue.use(directive) Vue.use(plugins) Vue.use(VueMeta) Vue.use(VueAMap); // 高德地图 DictData.install() /** * If you don't want to use mock-server * you want to use MockJs for mock api * you can execute: mockXHR() * * Currently MockJs will be used in the production environment, * please remove it before going online! ! ! */ // 修改 el-dialog 默认点击遮照为不关闭 Element.Dialog.props.closeOnClickModal.default = false Vue.use(Element, { size: Cookies.get('size') || 'medium' // set element-ui default size }) VueAMap.initAMapApiLoader({ key: '112049d76fe83e408d4ecceafb2ad4e3', plugin: ['AMap.Autocomplete', 'AMap.PlaceSearch', 'AMap.Scale', 'AMap.OverView', 'AMap.ToolBar', 'AMap.MapType', 'AMap.PolyEditor', 'AMap.CircleEditor'], // 默认高德 sdk 版本为 1.4.4 v: '1.4.4', uiVersion: '1.0.11' // 版本号 }); //配置安全密钥 window._AMapSecurityConfig = { securityJsCode: 'df0902aa53f34270d790ce32e8545f1e' //* 安全密钥 } Vue.config.productionTip = false new Vue({ el: '#app', router, store, render: h => h(App) }) ================================================ FILE: ruoyi-ui/src/permission.js ================================================ import router from './router' import store from './store' import { Message } from 'element-ui' import NProgress from 'nprogress' import 'nprogress/nprogress.css' import { getToken } from '@/utils/auth' import { isRelogin } from '@/utils/request' NProgress.configure({ showSpinner: false }) const whiteList = ['/login', '/register'] router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { to.meta.title && store.dispatch('settings/setTitle', to.meta.title) /* has token*/ if (to.path === '/login') { next({ path: '/' }) NProgress.done() } else { if (store.getters.roles.length === 0) { isRelogin.show = true // 判断当前用户是否已拉取完user_info信息 store.dispatch('GetInfo').then(() => { isRelogin.show = false store.dispatch('GenerateRoutes').then(accessRoutes => { // 根据roles权限生成可访问的路由表 router.addRoutes(accessRoutes) // 动态添加可访问路由表 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 }) }).catch(err => { store.dispatch('LogOut').then(() => { Message.error(err) next({ path: '/' }) }) }) } else { next() } } } else { // 没有token if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 next() } else { next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 NProgress.done() } } }) router.afterEach(() => { NProgress.done() }) ================================================ FILE: ruoyi-ui/src/plugins/auth.js ================================================ import store from '@/store' function authPermission(permission) { const all_permission = "*:*:*"; const permissions = store.getters && store.getters.permissions if (permission && permission.length > 0) { return permissions.some(v => { return all_permission === v || v === permission }) } else { return false } } function authRole(role) { const super_admin = "admin"; const roles = store.getters && store.getters.roles if (role && role.length > 0) { return roles.some(v => { return super_admin === v || v === role }) } else { return false } } export default { // 验证用户是否具备某权限 hasPermi(permission) { return authPermission(permission); }, // 验证用户是否含有指定权限,只需包含其中一个 hasPermiOr(permissions) { return permissions.some(item => { return authPermission(item) }) }, // 验证用户是否含有指定权限,必须全部拥有 hasPermiAnd(permissions) { return permissions.every(item => { return authPermission(item) }) }, // 验证用户是否具备某角色 hasRole(role) { return authRole(role); }, // 验证用户是否含有指定角色,只需包含其中一个 hasRoleOr(roles) { return roles.some(item => { return authRole(item) }) }, // 验证用户是否含有指定角色,必须全部拥有 hasRoleAnd(roles) { return roles.every(item => { return authRole(item) }) } } ================================================ FILE: ruoyi-ui/src/plugins/cache.js ================================================ const sessionCache = { set (key, value) { if (!sessionStorage) { return } if (key != null && value != null) { sessionStorage.setItem(key, value) } }, get (key) { if (!sessionStorage) { return null } if (key == null) { return null } return sessionStorage.getItem(key) }, setJSON (key, jsonValue) { if (jsonValue != null) { this.set(key, JSON.stringify(jsonValue)) } }, getJSON (key) { const value = this.get(key) if (value != null) { return JSON.parse(value) } }, remove (key) { sessionStorage.removeItem(key); } } const localCache = { set (key, value) { if (!localStorage) { return } if (key != null && value != null) { localStorage.setItem(key, value) } }, get (key) { if (!localStorage) { return null } if (key == null) { return null } return localStorage.getItem(key) }, setJSON (key, jsonValue) { if (jsonValue != null) { this.set(key, JSON.stringify(jsonValue)) } }, getJSON (key) { const value = this.get(key) if (value != null) { return JSON.parse(value) } }, remove (key) { localStorage.removeItem(key); } } export default { /** * 会话级缓存 */ session: sessionCache, /** * 本地缓存 */ local: localCache } ================================================ FILE: ruoyi-ui/src/plugins/download.js ================================================ import axios from 'axios' import {Loading, Message} from 'element-ui' import { saveAs } from 'file-saver' import { getToken } from '@/utils/auth' import errorCode from '@/utils/errorCode' import { blobValidate } from "@/utils/ruoyi"; const baseURL = process.env.VUE_APP_BASE_API let downloadLoadingInstance; export default { oss(ossId) { var url = baseURL + '/system/oss/download/' + ossId downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) axios({ method: 'get', url: url, responseType: 'blob', headers: { 'Authorization': 'Bearer ' + getToken() } }).then((res) => { const isBlob = blobValidate(res.data); if (isBlob) { const blob = new Blob([res.data], { type: 'application/octet-stream' }) this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) } else { this.printErrMsg(res.data); } downloadLoadingInstance.close(); }).catch((r) => { console.error(r) Message.error('下载文件出现错误,请联系管理员!') downloadLoadingInstance.close(); }) }, zip(url, name) { var url = baseURL + url axios({ method: 'get', url: url, responseType: 'blob', headers: { 'Authorization': 'Bearer ' + getToken(), 'datasource': localStorage.getItem("dataName") } }).then((res) => { const isBlob = blobValidate(res.data); if (isBlob) { const blob = new Blob([res.data], { type: 'application/zip' }) this.saveAs(blob, name) } else { this.printErrMsg(res.data); } }) }, saveAs(text, name, opts) { saveAs(text, name, opts); }, async printErrMsg(data) { const resText = await data.text(); const rspObj = JSON.parse(resText); const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] Message.error(errMsg); } } ================================================ FILE: ruoyi-ui/src/plugins/index.js ================================================ import tab from './tab' import auth from './auth' import cache from './cache' import modal from './modal' import download from './download' export default { install(Vue) { // 页签操作 Vue.prototype.$tab = tab // 认证对象 Vue.prototype.$auth = auth // 缓存对象 Vue.prototype.$cache = cache // 模态框对象 Vue.prototype.$modal = modal // 下载文件 Vue.prototype.$download = download } } ================================================ FILE: ruoyi-ui/src/plugins/modal.js ================================================ import { Message, MessageBox, Notification, Loading } from 'element-ui' let loadingInstance; export default { // 消息提示 msg(content) { Message.info(content) }, // 错误消息 msgError(content) { Message.error(content) }, // 成功消息 msgSuccess(content) { Message.success(content) }, // 警告消息 msgWarning(content) { Message.warning(content) }, // 弹出提示 alert(content) { MessageBox.alert(content, "系统提示") }, // 错误提示 alertError(content) { MessageBox.alert(content, "系统提示", { type: 'error' }) }, // 成功提示 alertSuccess(content) { MessageBox.alert(content, "系统提示", { type: 'success' }) }, // 警告提示 alertWarning(content) { MessageBox.alert(content, "系统提示", { type: 'warning' }) }, // 通知提示 notify(content) { Notification.info(content) }, // 错误通知 notifyError(content) { Notification.error(content); }, // 成功通知 notifySuccess(content) { Notification.success(content) }, // 警告通知 notifyWarning(content) { Notification.warning(content) }, // 确认窗体 confirm(content) { return MessageBox.confirm(content, "系统提示", { confirmButtonText: '确定', cancelButtonText: '取消', type: "warning", }) }, // 提交内容 prompt(content) { return MessageBox.prompt(content, "系统提示", { confirmButtonText: '确定', cancelButtonText: '取消', type: "warning", }) }, // 打开遮罩层 loading(content) { loadingInstance = Loading.service({ lock: true, text: content, spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) }, // 关闭遮罩层 closeLoading() { loadingInstance.close(); } } ================================================ FILE: ruoyi-ui/src/plugins/tab.js ================================================ import store from '@/store' import router from '@/router'; export default { // 刷新当前tab页签 refreshPage(obj) { const { path, query, matched } = router.currentRoute; if (obj === undefined) { matched.forEach((m) => { if (m.components && m.components.default && m.components.default.name) { if (!['Layout', 'ParentView'].includes(m.components.default.name)) { obj = { name: m.components.default.name, path: path, query: query }; } } }); } return store.dispatch('tagsView/delCachedView', obj).then(() => { const { path, query } = obj router.replace({ path: '/redirect' + path, query: query }) }) }, // 关闭当前tab页签,打开新页签 closeOpenPage(obj) { store.dispatch("tagsView/delView", router.currentRoute); if (obj !== undefined) { return router.push(obj); } }, // 关闭指定tab页签 closePage(obj) { if (obj === undefined) { return store.dispatch('tagsView/delView', router.currentRoute).then(({ visitedViews }) => { const latestView = visitedViews.slice(-1)[0] if (latestView) { return router.push(latestView.fullPath) } return router.push('/'); }); } return store.dispatch('tagsView/delView', obj); }, // 关闭所有tab页签 closeAllPage() { return store.dispatch('tagsView/delAllViews'); }, // 关闭左侧tab页签 closeLeftPage(obj) { return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute); }, // 关闭右侧tab页签 closeRightPage(obj) { return store.dispatch('tagsView/delRightTags', obj || router.currentRoute); }, // 关闭其他tab页签 closeOtherPage(obj) { return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute); }, // 添加tab页签 openPage(title, url, params) { var obj = { path: url, meta: { title: title } } store.dispatch('tagsView/addView', obj); return router.push({ path: url, query: params }); }, // 修改tab页签 updatePage(obj) { return store.dispatch('tagsView/updateVisitedView', obj); } } ================================================ FILE: ruoyi-ui/src/router/index.js ================================================ import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) /* Layout */ import Layout from '@/layout' /** * Note: 路由配置项 * * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 * roles: ['admin', 'common'] // 访问路由的角色权限 * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 * meta : { noCache: true // 如果设置为true,则不会被 缓存(默认 false) title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 } */ // 公共路由 export const constantRoutes = [ { path: '/redirect', component: Layout, hidden: true, children: [ { path: '/redirect/:path(.*)', component: () => import('@/views/redirect') } ] }, { path: '/login', component: () => import('@/views/login'), hidden: true }, { path: '/register', component: () => import('@/views/register'), hidden: true }, { path: '/404', component: () => import('@/views/error/404'), hidden: true }, { path: '/401', component: () => import('@/views/error/401'), hidden: true }, { path: '', component: Layout, redirect: 'index', children: [ { path: 'index', component: () => import('@/views/index'), name: 'Index', meta: { title: '首页', icon: 'dashboard', affix: true } } ] }, { path: '/user', component: Layout, hidden: true, redirect: 'noredirect', children: [ { path: 'profile', component: () => import('@/views/system/user/profile/index'), name: 'Profile', meta: { title: '个人中心', icon: 'user' } } ] } ] // 动态路由,基于用户权限动态去加载 export const dynamicRoutes = [ { path: '/system/user-auth', component: Layout, hidden: true, permissions: ['system:user:edit'], children: [ { path: 'role/:userId(\\d+)', component: () => import('@/views/system/user/authRole'), name: 'AuthRole', meta: { title: '分配角色', activeMenu: '/system/user' } } ] }, { path: '/system/role-auth', component: Layout, hidden: true, permissions: ['system:role:edit'], children: [ { path: 'user/:roleId(\\d+)', component: () => import('@/views/system/role/authUser'), name: 'AuthUser', meta: { title: '分配用户', activeMenu: '/system/role' } } ] }, { path: '/system/dict-data', component: Layout, hidden: true, permissions: ['system:dict:list'], children: [ { path: 'index/:dictId(\\d+)', component: () => import('@/views/system/dict/data'), name: 'Data', meta: { title: '字典数据', activeMenu: '/system/dict' } } ] }, { path: '/system/oss-config', component: Layout, hidden: true, permissions: ['system:oss:list'], children: [ { path: 'index', component: () => import('@/views/system/oss/config'), name: 'OssConfig', meta: { title: '配置管理', activeMenu: '/system/oss' } } ] }, { path: '/tool/gen-edit', component: Layout, hidden: true, permissions: ['tool:gen:edit'], children: [ { path: 'index/:tableId(\\d+)', component: () => import('@/views/tool/gen/editTable'), name: 'GenEdit', meta: { title: '修改生成配置', activeMenu: '/tool/gen' } } ] } ] // 防止连续点击多次路由报错 let routerPush = Router.prototype.push; let routerReplace = Router.prototype.replace; // push Router.prototype.push = function push(location) { return routerPush.call(this, location).catch(err => err) } // replace Router.prototype.replace = function push(location) { return routerReplace.call(this, location).catch(err => err) } export default new Router({ base: process.env.VUE_APP_CONTEXT_PATH, mode: 'history', // 去掉url中的# scrollBehavior: () => ({ y: 0 }), routes: constantRoutes }) ================================================ FILE: ruoyi-ui/src/settings.js ================================================ module.exports = { /** * 侧边栏主题 深色主题theme-dark,浅色主题theme-light */ sideTheme: 'theme-dark', /** * 是否系统布局配置 */ showSettings: false, /** * 是否显示顶部导航 */ topNav: false, /** * 是否显示 tagsView */ tagsView: true, /** * 是否固定头部 */ fixedHeader: false, /** * 是否显示logo */ sidebarLogo: true, /** * 是否显示动态标题 */ dynamicTitle: false, /** * @type {string | array} 'production' | ['production', 'development'] * @description Need show err logs component. * The default is only used in the production env * If you want to also use it in dev, you can pass ['production', 'development'] */ errorLog: 'production' } ================================================ FILE: ruoyi-ui/src/store/getters.js ================================================ const getters = { sidebar: state => state.app.sidebar, size: state => state.app.size, device: state => state.app.device, dict: state => state.dict.dict, visitedViews: state => state.tagsView.visitedViews, cachedViews: state => state.tagsView.cachedViews, token: state => state.user.token, avatar: state => state.user.avatar, name: state => state.user.name, introduction: state => state.user.introduction, roles: state => state.user.roles, permissions: state => state.user.permissions, permission_routes: state => state.permission.routes, topbarRouters:state => state.permission.topbarRouters, defaultRoutes:state => state.permission.defaultRoutes, sidebarRouters:state => state.permission.sidebarRouters, } export default getters ================================================ FILE: ruoyi-ui/src/store/index.js ================================================ import Vue from 'vue' import Vuex from 'vuex' import app from './modules/app' import dict from './modules/dict' import user from './modules/user' import tagsView from './modules/tagsView' import permission from './modules/permission' import settings from './modules/settings' import getters from './getters' Vue.use(Vuex) const store = new Vuex.Store({ modules: { app, dict, user, tagsView, permission, settings }, getters }) export default store ================================================ FILE: ruoyi-ui/src/store/modules/app.js ================================================ import Cookies from 'js-cookie' const state = { sidebar: { opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, withoutAnimation: false, hide: false }, device: 'desktop', size: Cookies.get('size') || 'medium' } const mutations = { TOGGLE_SIDEBAR: state => { if (state.sidebar.hide) { return false; } state.sidebar.opened = !state.sidebar.opened state.sidebar.withoutAnimation = false if (state.sidebar.opened) { Cookies.set('sidebarStatus', 1) } else { Cookies.set('sidebarStatus', 0) } }, CLOSE_SIDEBAR: (state, withoutAnimation) => { Cookies.set('sidebarStatus', 0) state.sidebar.opened = false state.sidebar.withoutAnimation = withoutAnimation }, TOGGLE_DEVICE: (state, device) => { state.device = device }, SET_SIZE: (state, size) => { state.size = size Cookies.set('size', size) }, SET_SIDEBAR_HIDE: (state, status) => { state.sidebar.hide = status } } const actions = { toggleSideBar({ commit }) { commit('TOGGLE_SIDEBAR') }, closeSideBar({ commit }, { withoutAnimation }) { commit('CLOSE_SIDEBAR', withoutAnimation) }, toggleDevice({ commit }, device) { commit('TOGGLE_DEVICE', device) }, setSize({ commit }, size) { commit('SET_SIZE', size) }, toggleSideBarHide({ commit }, status) { commit('SET_SIDEBAR_HIDE', status) } } export default { namespaced: true, state, mutations, actions } ================================================ FILE: ruoyi-ui/src/store/modules/dict.js ================================================ const state = { dict: new Array() } const mutations = { SET_DICT: (state, { key, value }) => { if (key !== null && key !== "") { state.dict.push({ key: key, value: value }) } }, REMOVE_DICT: (state, key) => { try { for (let i = 0; i < state.dict.length; i++) { if (state.dict[i].key == key) { state.dict.splice(i, i) return true } } } catch (e) { } }, CLEAN_DICT: (state) => { state.dict = new Array() } } const actions = { // 设置字典 setDict({ commit }, data) { commit('SET_DICT', data) }, // 删除字典 removeDict({ commit }, key) { commit('REMOVE_DICT', key) }, // 清空字典 cleanDict({ commit }) { commit('CLEAN_DICT') } } export default { namespaced: true, state, mutations, actions } ================================================ FILE: ruoyi-ui/src/store/modules/permission.js ================================================ import auth from '@/plugins/auth' import router, { constantRoutes, dynamicRoutes } from '@/router' import { getRouters } from '@/api/menu' import Layout from '@/layout/index' import ParentView from '@/components/ParentView' import InnerLink from '@/layout/components/InnerLink' const permission = { state: { routes: [], addRoutes: [], defaultRoutes: [], topbarRouters: [], sidebarRouters: [] }, mutations: { SET_ROUTES: (state, routes) => { state.addRoutes = routes state.routes = constantRoutes.concat(routes) }, SET_DEFAULT_ROUTES: (state, routes) => { state.defaultRoutes = constantRoutes.concat(routes) }, SET_TOPBAR_ROUTES: (state, routes) => { state.topbarRouters = routes }, SET_SIDEBAR_ROUTERS: (state, routes) => { state.sidebarRouters = routes }, }, actions: { // 生成路由 GenerateRoutes({ commit }) { return new Promise(resolve => { // 向后端请求路由数据 getRouters().then(res => { const sdata = JSON.parse(JSON.stringify(res.data)) const rdata = JSON.parse(JSON.stringify(res.data)) const sidebarRoutes = filterAsyncRouter(sdata) const rewriteRoutes = filterAsyncRouter(rdata, false, true) const asyncRoutes = filterDynamicRoutes(dynamicRoutes); rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true }) router.addRoutes(asyncRoutes); commit('SET_ROUTES', rewriteRoutes) commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes)) commit('SET_DEFAULT_ROUTES', sidebarRoutes) commit('SET_TOPBAR_ROUTES', sidebarRoutes) resolve(rewriteRoutes) }) }) } } } // 遍历后台传来的路由字符串,转换为组件对象 function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { return asyncRouterMap.filter(route => { if (type && route.children) { route.children = filterChildren(route.children) } if (route.component) { // Layout ParentView 组件特殊处理 if (route.component === 'Layout') { route.component = Layout } else if (route.component === 'ParentView') { route.component = ParentView } else if (route.component === 'InnerLink') { route.component = InnerLink } else { route.component = loadView(route.component) } } if (route.children != null && route.children && route.children.length) { route.children = filterAsyncRouter(route.children, route, type) } else { delete route['children'] delete route['redirect'] } return true }) } function filterChildren(childrenMap, lastRouter = false) { var children = [] childrenMap.forEach((el, index) => { if (el.children && el.children.length) { if (el.component === 'ParentView' && !lastRouter) { el.children.forEach(c => { c.path = el.path + '/' + c.path if (c.children && c.children.length) { children = children.concat(filterChildren(c.children, c)) return } children.push(c) }) return } } if (lastRouter) { el.path = lastRouter.path + '/' + el.path } children = children.concat(el) }) return children } // 动态路由遍历,验证是否具备权限 export function filterDynamicRoutes(routes) { const res = [] routes.forEach(route => { if (route.permissions) { if (auth.hasPermiOr(route.permissions)) { res.push(route) } } else if (route.roles) { if (auth.hasRoleOr(route.roles)) { res.push(route) } } }) return res } export const loadView = (view) => { if (process.env.NODE_ENV === 'development') { return (resolve) => require([`@/views/${view}`], resolve) } else { // 使用 import 实现生产环境的路由懒加载 return () => import(`@/views/${view}`) } } export default permission ================================================ FILE: ruoyi-ui/src/store/modules/settings.js ================================================ import defaultSettings from '@/settings' const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' const state = { title: '', theme: storageSetting.theme || '#409EFF', sideTheme: storageSetting.sideTheme || sideTheme, showSettings: showSettings, topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle } const mutations = { CHANGE_SETTING: (state, { key, value }) => { if (state.hasOwnProperty(key)) { state[key] = value } } } const actions = { // 修改布局设置 changeSetting({ commit }, data) { commit('CHANGE_SETTING', data) }, // 设置网页标题 setTitle({ commit }, title) { state.title = title } } export default { namespaced: true, state, mutations, actions } ================================================ FILE: ruoyi-ui/src/store/modules/tagsView.js ================================================ const state = { visitedViews: [], cachedViews: [], iframeViews: [] } const mutations = { ADD_IFRAME_VIEW: (state, view) => { if (state.iframeViews.some(v => v.path === view.path)) return state.iframeViews.push( Object.assign({}, view, { title: view.meta.title || 'no-name' }) ) }, ADD_VISITED_VIEW: (state, view) => { if (state.visitedViews.some(v => v.path === view.path)) return state.visitedViews.push( Object.assign({}, view, { title: view.meta.title || 'no-name' }) ) }, ADD_CACHED_VIEW: (state, view) => { if (state.cachedViews.includes(view.name)) return if (view.meta && !view.meta.noCache) { state.cachedViews.push(view.name) } }, DEL_VISITED_VIEW: (state, view) => { for (const [i, v] of state.visitedViews.entries()) { if (v.path === view.path) { state.visitedViews.splice(i, 1) break } } state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) }, DEL_IFRAME_VIEW: (state, view) => { state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) }, DEL_CACHED_VIEW: (state, view) => { const index = state.cachedViews.indexOf(view.name) index > -1 && state.cachedViews.splice(index, 1) }, DEL_OTHERS_VISITED_VIEWS: (state, view) => { state.visitedViews = state.visitedViews.filter(v => { return v.meta.affix || v.path === view.path }) state.iframeViews = state.iframeViews.filter(item => item.path === view.path) }, DEL_OTHERS_CACHED_VIEWS: (state, view) => { const index = state.cachedViews.indexOf(view.name) if (index > -1) { state.cachedViews = state.cachedViews.slice(index, index + 1) } else { state.cachedViews = [] } }, DEL_ALL_VISITED_VIEWS: state => { // keep affix tags const affixTags = state.visitedViews.filter(tag => tag.meta.affix) state.visitedViews = affixTags state.iframeViews = [] }, DEL_ALL_CACHED_VIEWS: state => { state.cachedViews = [] }, UPDATE_VISITED_VIEW: (state, view) => { for (let v of state.visitedViews) { if (v.path === view.path) { v = Object.assign(v, view) break } } }, DEL_RIGHT_VIEWS: (state, view) => { const index = state.visitedViews.findIndex(v => v.path === view.path) if (index === -1) { return } state.visitedViews = state.visitedViews.filter((item, idx) => { if (idx <= index || (item.meta && item.meta.affix)) { return true } const i = state.cachedViews.indexOf(item.name) if (i > -1) { state.cachedViews.splice(i, 1) } if(item.meta.link) { const fi = state.iframeViews.findIndex(v => v.path === item.path) state.iframeViews.splice(fi, 1) } return false }) }, DEL_LEFT_VIEWS: (state, view) => { const index = state.visitedViews.findIndex(v => v.path === view.path) if (index === -1) { return } state.visitedViews = state.visitedViews.filter((item, idx) => { if (idx >= index || (item.meta && item.meta.affix)) { return true } const i = state.cachedViews.indexOf(item.name) if (i > -1) { state.cachedViews.splice(i, 1) } if(item.meta.link) { const fi = state.iframeViews.findIndex(v => v.path === item.path) state.iframeViews.splice(fi, 1) } return false }) } } const actions = { addView({ dispatch }, view) { dispatch('addVisitedView', view) dispatch('addCachedView', view) }, addIframeView({ commit }, view) { commit('ADD_IFRAME_VIEW', view) }, addVisitedView({ commit }, view) { commit('ADD_VISITED_VIEW', view) }, addCachedView({ commit }, view) { commit('ADD_CACHED_VIEW', view) }, delView({ dispatch, state }, view) { return new Promise(resolve => { dispatch('delVisitedView', view) dispatch('delCachedView', view) resolve({ visitedViews: [...state.visitedViews], cachedViews: [...state.cachedViews] }) }) }, delVisitedView({ commit, state }, view) { return new Promise(resolve => { commit('DEL_VISITED_VIEW', view) resolve([...state.visitedViews]) }) }, delIframeView({ commit, state }, view) { return new Promise(resolve => { commit('DEL_IFRAME_VIEW', view) resolve([...state.iframeViews]) }) }, delCachedView({ commit, state }, view) { return new Promise(resolve => { commit('DEL_CACHED_VIEW', view) resolve([...state.cachedViews]) }) }, delOthersViews({ dispatch, state }, view) { return new Promise(resolve => { dispatch('delOthersVisitedViews', view) dispatch('delOthersCachedViews', view) resolve({ visitedViews: [...state.visitedViews], cachedViews: [...state.cachedViews] }) }) }, delOthersVisitedViews({ commit, state }, view) { return new Promise(resolve => { commit('DEL_OTHERS_VISITED_VIEWS', view) resolve([...state.visitedViews]) }) }, delOthersCachedViews({ commit, state }, view) { return new Promise(resolve => { commit('DEL_OTHERS_CACHED_VIEWS', view) resolve([...state.cachedViews]) }) }, delAllViews({ dispatch, state }, view) { return new Promise(resolve => { dispatch('delAllVisitedViews', view) dispatch('delAllCachedViews', view) resolve({ visitedViews: [...state.visitedViews], cachedViews: [...state.cachedViews] }) }) }, delAllVisitedViews({ commit, state }) { return new Promise(resolve => { commit('DEL_ALL_VISITED_VIEWS') resolve([...state.visitedViews]) }) }, delAllCachedViews({ commit, state }) { return new Promise(resolve => { commit('DEL_ALL_CACHED_VIEWS') resolve([...state.cachedViews]) }) }, updateVisitedView({ commit }, view) { commit('UPDATE_VISITED_VIEW', view) }, delRightTags({ commit }, view) { return new Promise(resolve => { commit('DEL_RIGHT_VIEWS', view) resolve([...state.visitedViews]) }) }, delLeftTags({ commit }, view) { return new Promise(resolve => { commit('DEL_LEFT_VIEWS', view) resolve([...state.visitedViews]) }) }, } export default { namespaced: true, state, mutations, actions } ================================================ FILE: ruoyi-ui/src/store/modules/user.js ================================================ import { login, logout, getInfo } from '@/api/login' import { getToken, setToken, removeToken } from '@/utils/auth' const user = { state: { token: getToken(), name: '', avatar: '', roles: [], permissions: [] }, mutations: { SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: (state, name) => { state.name = name }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }, SET_ROLES: (state, roles) => { state.roles = roles }, SET_PERMISSIONS: (state, permissions) => { state.permissions = permissions } }, actions: { // 登录 Login({ commit }, userInfo) { const username = userInfo.username.trim() const password = userInfo.password const code = userInfo.code const uuid = userInfo.uuid return new Promise((resolve, reject) => { login(username, password, code, uuid).then(res => { setToken(res.data.token) commit('SET_TOKEN', res.data.token) resolve() }).catch(error => { reject(error) }) }) }, // 获取用户信息 GetInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo().then(res => { const user = res.data.user const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/1.png") : user.avatar; if (res.data.roles && res.data.roles.length > 0) { // 验证返回的roles是否是一个非空数组 commit('SET_ROLES', res.data.roles) commit('SET_PERMISSIONS', res.data.permissions) } else { commit('SET_ROLES', ['ROLE_DEFAULT']) } commit('SET_NAME', user.userName) commit('SET_AVATAR', avatar) resolve(res) }).catch(error => { reject(error) }) }) }, // 退出系统 LogOut({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { commit('SET_TOKEN', '') commit('SET_ROLES', []) commit('SET_PERMISSIONS', []) removeToken() resolve() }).catch(error => { reject(error) }) }) }, // 前端 登出 FedLogOut({ commit }) { return new Promise(resolve => { commit('SET_TOKEN', '') removeToken() resolve() }) } } } export default user ================================================ FILE: ruoyi-ui/src/utils/auth.js ================================================ import Cookies from 'js-cookie' const TokenKey = 'Admin-Token' export function getToken() { return Cookies.get(TokenKey) } export function setToken(token) { return Cookies.set(TokenKey, token) } export function removeToken() { return Cookies.remove(TokenKey) } ================================================ FILE: ruoyi-ui/src/utils/dict/Dict.js ================================================ import Vue from 'vue' import { mergeRecursive } from "@/utils/ruoyi"; import DictMeta from './DictMeta' import DictData from './DictData' const DEFAULT_DICT_OPTIONS = { types: [], } /** * @classdesc 字典 * @property {Object} label 标签对象,内部属性名为字典类型名称 * @property {Object} dict 字段数组,内部属性名为字典类型名称 * @property {Array.} _dictMetas 字典元数据数组 */ export default class Dict { constructor() { this.owner = null this.label = {} this.type = {} } init(options) { if (options instanceof Array) { options = { types: options } } const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options) if (opts.types === undefined) { throw new Error('need dict types') } const ps = [] this._dictMetas = opts.types.map(t => DictMeta.parse(t)) this._dictMetas.forEach(dictMeta => { const type = dictMeta.type Vue.set(this.label, type, {}) Vue.set(this.type, type, []) if (dictMeta.lazy) { return } ps.push(loadDict(this, dictMeta)) }) return Promise.all(ps) } /** * 重新加载字典 * @param {String} type 字典类型 */ reloadDict(type) { const dictMeta = this._dictMetas.find(e => e.type === type) if (dictMeta === undefined) { return Promise.reject(`the dict meta of ${type} was not found`) } return loadDict(this, dictMeta) } } /** * 加载字典 * @param {Dict} dict 字典 * @param {DictMeta} dictMeta 字典元数据 * @returns {Promise} */ function loadDict(dict, dictMeta) { return dictMeta.request(dictMeta) .then(response => { const type = dictMeta.type let dicts = dictMeta.responseConverter(response, dictMeta) if (!(dicts instanceof Array)) { console.error('the return of responseConverter must be Array.') dicts = [] } else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) { console.error('the type of elements in dicts must be DictData') dicts = [] } dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts) dicts.forEach(d => { Vue.set(dict.label[type], d.value, d.label) }) return dicts }) } ================================================ FILE: ruoyi-ui/src/utils/dict/DictConverter.js ================================================ import DictOptions from './DictOptions' import DictData from './DictData' export default function(dict, dictMeta) { const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS) const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS) return new DictData(dict[label], dict[value], dict) } /** * 确定字典字段 * @param {DictData} dict * @param {...String} fields */ function determineDictField(dict, ...fields) { return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f)) } ================================================ FILE: ruoyi-ui/src/utils/dict/DictData.js ================================================ /** * @classdesc 字典数据 * @property {String} label 标签 * @property {*} value 标签 * @property {Object} raw 原始数据 */ export default class DictData { constructor(label, value, raw) { this.label = label this.value = value this.raw = raw } } ================================================ FILE: ruoyi-ui/src/utils/dict/DictMeta.js ================================================ import { mergeRecursive } from "@/utils/ruoyi"; import DictOptions from './DictOptions' /** * @classdesc 字典元数据 * @property {String} type 类型 * @property {Function} request 请求 * @property {String} label 标签字段 * @property {String} value 值字段 */ export default class DictMeta { constructor(options) { this.type = options.type this.request = options.request this.responseConverter = options.responseConverter this.labelField = options.labelField this.valueField = options.valueField this.lazy = options.lazy === true } } /** * 解析字典元数据 * @param {Object} options * @returns {DictMeta} */ DictMeta.parse= function(options) { let opts = null if (typeof options === 'string') { opts = DictOptions.metas[options] || {} opts.type = options } else if (typeof options === 'object') { opts = options } opts = mergeRecursive(DictOptions.metas['*'], opts) return new DictMeta(opts) } ================================================ FILE: ruoyi-ui/src/utils/dict/DictOptions.js ================================================ import { mergeRecursive } from "@/utils/ruoyi"; import dictConverter from './DictConverter' export const options = { metas: { '*': { /** * 字典请求,方法签名为function(dictMeta: DictMeta): Promise */ request: (dictMeta) => { console.log(`load dict ${dictMeta.type}`) return Promise.resolve([]) }, /** * 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData */ responseConverter, labelField: 'label', valueField: 'value', }, }, /** * 默认标签字段 */ DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'], /** * 默认值字段 */ DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'], } /** * 映射字典 * @param {Object} response 字典数据 * @param {DictMeta} dictMeta 字典元数据 * @returns {DictData} */ function responseConverter(response, dictMeta) { const dicts = response.content instanceof Array ? response.content : response if (dicts === undefined) { console.warn(`no dict data of "${dictMeta.type}" found in the response`) return [] } return dicts.map(d => dictConverter(d, dictMeta)) } export function mergeOptions(src) { mergeRecursive(options, src) } export default options ================================================ FILE: ruoyi-ui/src/utils/dict/index.js ================================================ import Dict from './Dict' import { mergeOptions } from './DictOptions' export default function(Vue, options) { mergeOptions(options) Vue.mixin({ data() { if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) { return {} } const dict = new Dict() dict.owner = this return { dict } }, created() { if (!(this.dict instanceof Dict)) { return } options.onCreated && options.onCreated(this.dict) this.dict.init(this.$options.dicts).then(() => { options.onReady && options.onReady(this.dict) this.$nextTick(() => { this.$emit('dictReady', this.dict) if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) { this.$options.methods.onDictReady.call(this, this.dict) } }) }) }, }) } ================================================ FILE: ruoyi-ui/src/utils/errorCode.js ================================================ export default { '401': '认证失败,无法访问系统资源', '403': '当前操作没有权限', '404': '访问资源不存在', 'default': '系统未知错误,请反馈给管理员' } ================================================ FILE: ruoyi-ui/src/utils/generator/config.js ================================================ export const formConf = { formRef: 'elForm', formModel: 'formData', size: 'medium', labelPosition: 'right', labelWidth: 100, formRules: 'rules', gutter: 15, disabled: false, span: 24, formBtns: true } export const inputComponents = [ { label: '单行文本', tag: 'el-input', tagIcon: 'input', placeholder: '请输入', defaultValue: undefined, span: 24, labelWidth: null, style: { width: '100%' }, clearable: true, prepend: '', append: '', 'prefix-icon': '', 'suffix-icon': '', maxlength: null, 'show-word-limit': false, readonly: false, disabled: false, required: true, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/input' }, { label: '多行文本', tag: 'el-input', tagIcon: 'textarea', type: 'textarea', placeholder: '请输入', defaultValue: undefined, span: 24, labelWidth: null, autosize: { minRows: 4, maxRows: 4 }, style: { width: '100%' }, maxlength: null, 'show-word-limit': false, readonly: false, disabled: false, required: true, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/input' }, { label: '密码', tag: 'el-input', tagIcon: 'password', placeholder: '请输入', defaultValue: undefined, span: 24, 'show-password': true, labelWidth: null, style: { width: '100%' }, clearable: true, prepend: '', append: '', 'prefix-icon': '', 'suffix-icon': '', maxlength: null, 'show-word-limit': false, readonly: false, disabled: false, required: true, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/input' }, { label: '计数器', tag: 'el-input-number', tagIcon: 'number', placeholder: '', defaultValue: undefined, span: 24, labelWidth: null, min: undefined, max: undefined, step: undefined, 'step-strictly': false, precision: undefined, 'controls-position': '', disabled: false, required: true, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/input-number' } ] export const selectComponents = [ { label: '下拉选择', tag: 'el-select', tagIcon: 'select', placeholder: '请选择', defaultValue: undefined, span: 24, labelWidth: null, style: { width: '100%' }, clearable: true, disabled: false, required: true, filterable: false, multiple: false, options: [{ label: '选项一', value: 1 }, { label: '选项二', value: 2 }], regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/select' }, { label: '级联选择', tag: 'el-cascader', tagIcon: 'cascader', placeholder: '请选择', defaultValue: [], span: 24, labelWidth: null, style: { width: '100%' }, props: { props: { multiple: false } }, 'show-all-levels': true, disabled: false, clearable: true, filterable: false, required: true, options: [{ id: 1, value: 1, label: '选项1', children: [{ id: 2, value: 2, label: '选项1-1' }] }], dataType: 'dynamic', labelKey: 'label', valueKey: 'value', childrenKey: 'children', separator: '/', regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/cascader' }, { label: '单选框组', tag: 'el-radio-group', tagIcon: 'radio', defaultValue: undefined, span: 24, labelWidth: null, style: {}, optionType: 'default', border: false, size: 'medium', disabled: false, required: true, options: [{ label: '选项一', value: 1 }, { label: '选项二', value: 2 }], regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/radio' }, { label: '多选框组', tag: 'el-checkbox-group', tagIcon: 'checkbox', defaultValue: [], span: 24, labelWidth: null, style: {}, optionType: 'default', border: false, size: 'medium', disabled: false, required: true, options: [{ label: '选项一', value: 1 }, { label: '选项二', value: 2 }], regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/checkbox' }, { label: '开关', tag: 'el-switch', tagIcon: 'switch', defaultValue: false, span: 24, labelWidth: null, style: {}, disabled: false, required: true, 'active-text': '', 'inactive-text': '', 'active-color': null, 'inactive-color': null, 'active-value': true, 'inactive-value': false, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/switch' }, { label: '滑块', tag: 'el-slider', tagIcon: 'slider', defaultValue: null, span: 24, labelWidth: null, disabled: false, required: true, min: 0, max: 100, step: 1, 'show-stops': false, range: false, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/slider' }, { label: '时间选择', tag: 'el-time-picker', tagIcon: 'time', placeholder: '请选择', defaultValue: null, span: 24, labelWidth: null, style: { width: '100%' }, disabled: false, clearable: true, required: true, 'picker-options': { selectableRange: '00:00:00-23:59:59' }, format: 'HH:mm:ss', 'value-format': 'HH:mm:ss', regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' }, { label: '时间范围', tag: 'el-time-picker', tagIcon: 'time-range', defaultValue: null, span: 24, labelWidth: null, style: { width: '100%' }, disabled: false, clearable: true, required: true, 'is-range': true, 'range-separator': '至', 'start-placeholder': '开始时间', 'end-placeholder': '结束时间', format: 'HH:mm:ss', 'value-format': 'HH:mm:ss', regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' }, { label: '日期选择', tag: 'el-date-picker', tagIcon: 'date', placeholder: '请选择', defaultValue: null, type: 'date', span: 24, labelWidth: null, style: { width: '100%' }, disabled: false, clearable: true, required: true, format: 'yyyy-MM-dd', 'value-format': 'yyyy-MM-dd', readonly: false, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' }, { label: '日期范围', tag: 'el-date-picker', tagIcon: 'date-range', defaultValue: null, span: 24, labelWidth: null, style: { width: '100%' }, type: 'daterange', 'range-separator': '至', 'start-placeholder': '开始日期', 'end-placeholder': '结束日期', disabled: false, clearable: true, required: true, format: 'yyyy-MM-dd', 'value-format': 'yyyy-MM-dd', readonly: false, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' }, { label: '评分', tag: 'el-rate', tagIcon: 'rate', defaultValue: 0, span: 24, labelWidth: null, style: {}, max: 5, 'allow-half': false, 'show-text': false, 'show-score': false, disabled: false, required: true, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/rate' }, { label: '颜色选择', tag: 'el-color-picker', tagIcon: 'color', defaultValue: null, labelWidth: null, 'show-alpha': false, 'color-format': '', disabled: false, required: true, size: 'medium', regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/color-picker' }, { label: '上传', tag: 'el-upload', tagIcon: 'upload', action: 'https://jsonplaceholder.typicode.com/posts/', defaultValue: null, labelWidth: null, disabled: false, required: true, accept: '', name: 'file', 'auto-upload': true, showTip: false, buttonText: '点击上传', fileSize: 2, sizeUnit: 'MB', 'list-type': 'text', multiple: false, regList: [], changeTag: true, document: 'https://element.eleme.cn/#/zh-CN/component/upload' } ] export const layoutComponents = [ { layout: 'rowFormItem', tagIcon: 'row', type: 'default', justify: 'start', align: 'top', label: '行容器', layoutTree: true, children: [], document: 'https://element.eleme.cn/#/zh-CN/component/layout' }, { layout: 'colFormItem', label: '按钮', changeTag: true, labelWidth: null, tag: 'el-button', tagIcon: 'button', span: 24, default: '主要按钮', type: 'primary', icon: 'el-icon-search', size: 'medium', disabled: false, document: 'https://element.eleme.cn/#/zh-CN/component/button' } ] // 组件rule的触发方式,无触发方式的组件不生成rule export const trigger = { 'el-input': 'blur', 'el-input-number': 'blur', 'el-select': 'change', 'el-radio-group': 'change', 'el-checkbox-group': 'change', 'el-cascader': 'change', 'el-time-picker': 'change', 'el-date-picker': 'change', 'el-rate': 'change' } ================================================ FILE: ruoyi-ui/src/utils/generator/css.js ================================================ const styles = { 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}', 'el-upload': '.el-upload__tip{line-height: 1.2;}' } function addCss(cssList, el) { const css = styles[el.tag] css && cssList.indexOf(css) === -1 && cssList.push(css) if (el.children) { el.children.forEach(el2 => addCss(cssList, el2)) } } export function makeUpCss(conf) { const cssList = [] conf.fields.forEach(el => addCss(cssList, el)) return cssList.join('\n') } ================================================ FILE: ruoyi-ui/src/utils/generator/drawingDefault.js ================================================ export default [ { layout: 'colFormItem', tagIcon: 'input', label: '手机号', vModel: 'mobile', formId: 6, tag: 'el-input', placeholder: '请输入手机号', defaultValue: '', span: 24, style: { width: '100%' }, clearable: true, prepend: '', append: '', 'prefix-icon': 'el-icon-mobile', 'suffix-icon': '', maxlength: 11, 'show-word-limit': true, readonly: false, disabled: false, required: true, changeTag: true, regList: [{ pattern: '/^1(3|4|5|7|8|9)\\d{9}$/', message: '手机号格式错误' }] } ] ================================================ FILE: ruoyi-ui/src/utils/generator/html.js ================================================ /* eslint-disable max-len */ import { trigger } from './config' let confGlobal let someSpanIsNot24 export function dialogWrapper(str) { return ` ${str}
        取消 确定
        ` } export function vueTemplate(str) { return `` } export function vueScript(str) { return `` } export function cssStyle(cssStr) { return `` } function buildFormTemplate(conf, child, type) { let labelPosition = '' if (conf.labelPosition !== 'right') { labelPosition = `label-position="${conf.labelPosition}"` } const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' let str = ` ${child} ${buildFromBtns(conf, type)} ` if (someSpanIsNot24) { str = ` ${str} ` } return str } function buildFromBtns(conf, type) { let str = '' if (conf.formBtns && type === 'file') { str = ` 提交 重置 ` if (someSpanIsNot24) { str = ` ${str} ` } } return str } // span不为24的用el-col包裹 function colWrapper(element, str) { if (someSpanIsNot24 || element.span !== 24) { return ` ${str} ` } return str } const layouts = { colFormItem(element) { let labelWidth = '' if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { labelWidth = `label-width="${element.labelWidth}px"` } const required = !trigger[element.tag] && element.required ? 'required' : '' const tagDom = tags[element.tag] ? tags[element.tag](element) : null let str = ` ${tagDom} ` str = colWrapper(element, str) return str }, rowFormItem(element) { const type = element.type === 'default' ? '' : `type="${element.type}"` const justify = element.type === 'default' ? '' : `justify="${element.justify}"` const align = element.type === 'default' ? '' : `align="${element.align}"` const gutter = element.gutter ? `gutter="${element.gutter}"` : '' const children = element.children.map(el => layouts[el.layout](el)) let str = ` ${children.join('\n')} ` str = colWrapper(element, str) return str } } const tags = { 'el-button': el => { const { tag, disabled } = attrBuilder(el) const type = el.type ? `type="${el.type}"` : '' const icon = el.icon ? `icon="${el.icon}"` : '' const size = el.size ? `size="${el.size}"` : '' let child = buildElButtonChild(el) if (child) child = `\n${child}\n` // 换行 return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}` }, 'el-input': el => { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' const readonly = el.readonly ? 'readonly' : '' const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : '' const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' const showPassword = el['show-password'] ? 'show-password' : '' const type = el.type ? `type="${el.type}"` : '' const autosize = el.autosize && el.autosize.minRows ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` : '' let child = buildElInputChild(el) if (child) child = `\n${child}\n` // 换行 return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` }, 'el-input-number': el => { const { disabled, vModel, placeholder } = attrBuilder(el) const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' const min = el.min ? `:min='${el.min}'` : '' const max = el.max ? `:max='${el.max}'` : '' const step = el.step ? `:step='${el.step}'` : '' const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' const precision = el.precision ? `:precision='${el.precision}'` : '' return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` }, 'el-select': el => { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const filterable = el.filterable ? 'filterable' : '' const multiple = el.multiple ? 'multiple' : '' let child = buildElSelectChild(el) if (child) child = `\n${child}\n` // 换行 return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` }, 'el-radio-group': el => { const { disabled, vModel } = attrBuilder(el) const size = `size="${el.size}"` let child = buildElRadioGroupChild(el) if (child) child = `\n${child}\n` // 换行 return `<${el.tag} ${vModel} ${size} ${disabled}>${child}` }, 'el-checkbox-group': el => { const { disabled, vModel } = attrBuilder(el) const size = `size="${el.size}"` const min = el.min ? `:min="${el.min}"` : '' const max = el.max ? `:max="${el.max}"` : '' let child = buildElCheckboxGroupChild(el) if (child) child = `\n${child}\n` // 换行 return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` }, 'el-switch': el => { const { disabled, vModel } = attrBuilder(el) const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` }, 'el-cascader': el => { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const options = el.options ? `:options="${el.vModel}Options"` : '' const props = el.props ? `:props="${el.vModel}Props"` : '' const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' const filterable = el.filterable ? 'filterable' : '' const separator = el.separator === '/' ? '' : `separator="${el.separator}"` return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` }, 'el-slider': el => { const { disabled, vModel } = attrBuilder(el) const min = el.min ? `:min='${el.min}'` : '' const max = el.max ? `:max='${el.max}'` : '' const step = el.step ? `:step='${el.step}'` : '' const range = el.range ? 'range' : '' const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` }, 'el-time-picker': el => { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' const isRange = el['is-range'] ? 'is-range' : '' const format = el.format ? `format="${el.format}"` : '' const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` }, 'el-date-picker': el => { const { disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' const format = el.format ? `format="${el.format}"` : '' const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' const type = el.type === 'date' ? '' : `type="${el.type}"` const readonly = el.readonly ? 'readonly' : '' return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` }, 'el-rate': el => { const { disabled, vModel } = attrBuilder(el) const max = el.max ? `:max='${el.max}'` : '' const allowHalf = el['allow-half'] ? 'allow-half' : '' const showText = el['show-text'] ? 'show-text' : '' const showScore = el['show-score'] ? 'show-score' : '' return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>` }, 'el-color-picker': el => { const { disabled, vModel } = attrBuilder(el) const size = `size="${el.size}"` const showAlpha = el['show-alpha'] ? 'show-alpha' : '' const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` }, 'el-upload': el => { const disabled = el.disabled ? ':disabled=\'true\'' : '' const action = el.action ? `:action="${el.vModel}Action"` : '' const multiple = el.multiple ? 'multiple' : '' const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' const accept = el.accept ? `accept="${el.accept}"` : '' const name = el.name !== 'file' ? `name="${el.name}"` : '' const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` const fileList = `:file-list="${el.vModel}fileList"` const ref = `ref="${el.vModel}"` let child = buildElUploadChild(el) if (child) child = `\n${child}\n` // 换行 return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` } } function attrBuilder(el) { return { vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, clearable: el.clearable ? 'clearable' : '', placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', disabled: el.disabled ? ':disabled=\'true\'' : '' } } // el-buttin 子级 function buildElButtonChild(conf) { const children = [] if (conf.default) { children.push(conf.default) } return children.join('\n') } // el-input innerHTML function buildElInputChild(conf) { const children = [] if (conf.prepend) { children.push(``) } if (conf.append) { children.push(``) } return children.join('\n') } function buildElSelectChild(conf) { const children = [] if (conf.options && conf.options.length) { children.push(``) } return children.join('\n') } function buildElRadioGroupChild(conf) { const children = [] if (conf.options && conf.options.length) { const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' const border = conf.border ? 'border' : '' children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) } return children.join('\n') } function buildElCheckboxGroupChild(conf) { const children = [] if (conf.options && conf.options.length) { const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' const border = conf.border ? 'border' : '' children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) } return children.join('\n') } function buildElUploadChild(conf) { const list = [] if (conf['list-type'] === 'picture-card') list.push('') else list.push(`${conf.buttonText}`) if (conf.showTip) list.push(`
        只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
        `) return list.join('\n') } export function makeUpHtml(conf, type) { const htmlList = [] confGlobal = conf someSpanIsNot24 = conf.fields.some(item => item.span !== 24) conf.fields.forEach(el => { htmlList.push(layouts[el.layout](el)) }) const htmlStr = htmlList.join('\n') let temp = buildFormTemplate(conf, htmlStr, type) if (type === 'dialog') { temp = dialogWrapper(temp) } confGlobal = null return temp } ================================================ FILE: ruoyi-ui/src/utils/generator/icon.json ================================================ ["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"] ================================================ FILE: ruoyi-ui/src/utils/generator/js.js ================================================ import { isArray } from 'util' import { exportDefault, titleCase } from '@/utils/index' import { trigger } from './config' const units = { KB: '1024', MB: '1024 / 1024', GB: '1024 / 1024 / 1024' } let confGlobal const inheritAttrs = { file: '', dialog: 'inheritAttrs: false,' } export function makeUpJs(conf, type) { confGlobal = conf = JSON.parse(JSON.stringify(conf)) const dataList = [] const ruleList = [] const optionsList = [] const propsList = [] const methodList = mixinMethod(type) const uploadVarList = [] conf.fields.forEach(el => { buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) }) const script = buildexport( conf, type, dataList.join('\n'), ruleList.join('\n'), optionsList.join('\n'), uploadVarList.join('\n'), propsList.join('\n'), methodList.join('\n') ) confGlobal = null return script } function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) { buildData(el, dataList) buildRules(el, ruleList) if (el.options && el.options.length) { buildOptions(el, optionsList) if (el.dataType === 'dynamic') { const model = `${el.vModel}Options` const options = titleCase(model) buildOptionMethod(`get${options}`, model, methodList) } } if (el.props && el.props.props) { buildProps(el, propsList) } if (el.action && el.tag === 'el-upload') { uploadVarList.push( `${el.vModel}Action: '${el.action}', ${el.vModel}fileList: [],` ) methodList.push(buildBeforeUpload(el)) if (!el['auto-upload']) { methodList.push(buildSubmitUpload(el)) } } if (el.children) { el.children.forEach(el2 => { buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) }) } } function mixinMethod(type) { const list = []; const minxins = { file: confGlobal.formBtns ? { submitForm: `submitForm() { this.$refs['${confGlobal.formRef}'].validate(valid => { if(!valid) return // TODO 提交表单 }) },`, resetForm: `resetForm() { this.$refs['${confGlobal.formRef}'].resetFields() },` } : null, dialog: { onOpen: 'onOpen() {},', onClose: `onClose() { this.$refs['${confGlobal.formRef}'].resetFields() },`, close: `close() { this.$emit('update:visible', false) },`, handleConfirm: `handleConfirm() { this.$refs['${confGlobal.formRef}'].validate(valid => { if(!valid) return this.close() }) },` } } const methods = minxins[type] if (methods) { Object.keys(methods).forEach(key => { list.push(methods[key]) }) } return list } function buildData(conf, dataList) { if (conf.vModel === undefined) return let defaultValue if (typeof (conf.defaultValue) === 'string' && !conf.multiple) { defaultValue = `'${conf.defaultValue}'` } else { defaultValue = `${JSON.stringify(conf.defaultValue)}` } dataList.push(`${conf.vModel}: ${defaultValue},`) } function buildRules(conf, ruleList) { if (conf.vModel === undefined) return const rules = [] if (trigger[conf.tag]) { if (conf.required) { const type = isArray(conf.defaultValue) ? 'type: \'array\',' : '' let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder if (message === undefined) message = `${conf.label}不能为空` rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`) } if (conf.regList && isArray(conf.regList)) { conf.regList.forEach(item => { if (item.pattern) { rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`) } }) } ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) } } function buildOptions(conf, optionsList) { if (conf.vModel === undefined) return if (conf.dataType === 'dynamic') { conf.options = [] } const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},` optionsList.push(str) } function buildProps(conf, propsList) { if (conf.dataType === 'dynamic') { conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey) conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey) conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey) } const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},` propsList.push(str) } function buildBeforeUpload(conf) { const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const returnList = [] if (conf.fileSize) { rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} if(!isRightSize){ this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') }` returnList.push('isRightSize') } if (conf.accept) { acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) if(!isAccept){ this.$message.error('应该选择${conf.accept}类型的文件') }` returnList.push('isAccept') } const str = `${conf.vModel}BeforeUpload(file) { ${rightSizeCode} ${acceptCode} return ${returnList.join('&&')} },` return returnList.length ? str : '' } function buildSubmitUpload(conf) { const str = `submitUpload() { this.$refs['${conf.vModel}'].submit() },` return str } function buildOptionMethod(methodName, model, methodList) { const str = `${methodName}() { // TODO 发起请求获取数据 this.${model} },` methodList.push(str) } function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) { const str = `${exportDefault}{ ${inheritAttrs[type]} components: {}, props: [], data () { return { ${conf.formModel}: { ${data} }, ${conf.formRules}: { ${rules} }, ${uploadVar} ${selectOptions} ${props} } }, computed: {}, watch: {}, created () {}, mounted () {}, methods: { ${methods} } }` return str } ================================================ FILE: ruoyi-ui/src/utils/generator/render.js ================================================ import { makeMap } from '@/utils/index' // 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js const isAttr = makeMap( 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' + 'target,title,type,usemap,value,width,wrap' ) function vModel(self, dataObject, defaultValue) { dataObject.props.value = defaultValue dataObject.on.input = val => { self.$emit('input', val) } } const componentChild = { 'el-button': { default(h, conf, key) { return conf[key] }, }, 'el-input': { prepend(h, conf, key) { return }, append(h, conf, key) { return } }, 'el-select': { options(h, conf, key) { const list = [] conf.options.forEach(item => { list.push() }) return list } }, 'el-radio-group': { options(h, conf, key) { const list = [] conf.options.forEach(item => { if (conf.optionType === 'button') list.push({item.label}) else list.push({item.label}) }) return list } }, 'el-checkbox-group': { options(h, conf, key) { const list = [] conf.options.forEach(item => { if (conf.optionType === 'button') { list.push({item.label}) } else { list.push({item.label}) } }) return list } }, 'el-upload': { 'list-type': (h, conf, key) => { const list = [] if (conf['list-type'] === 'picture-card') { list.push() } else { list.push({conf.buttonText}) } if (conf.showTip) { list.push(
        只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件
        ) } return list } } } export default { render(h) { const dataObject = { attrs: {}, props: {}, on: {}, style: {} } const confClone = JSON.parse(JSON.stringify(this.conf)) const children = [] const childObjs = componentChild[confClone.tag] if (childObjs) { Object.keys(childObjs).forEach(key => { const childFunc = childObjs[key] if (confClone[key]) { children.push(childFunc(h, confClone, key)) } }) } Object.keys(confClone).forEach(key => { const val = confClone[key] if (key === 'vModel') { vModel(this, dataObject, confClone.defaultValue) } else if (dataObject[key]) { dataObject[key] = val } else if (!isAttr(key)) { dataObject.props[key] = val } else { dataObject.attrs[key] = val } }) return h(this.conf.tag, dataObject, children) }, props: ['conf'] } ================================================ FILE: ruoyi-ui/src/utils/index.js ================================================ import { parseTime } from './ruoyi' /** * 表格时间格式化 */ export function formatDate(cellValue) { if (cellValue == null || cellValue == "") return ""; var date = new Date(cellValue) var year = date.getFullYear() var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds } /** * @param {number} time * @param {string} option * @returns {string} */ export function formatTime(time, option) { if (('' + time).length === 10) { time = parseInt(time) * 1000 } else { time = +time } const d = new Date(time) const now = Date.now() const diff = (now - d) / 1000 if (diff < 30) { return '刚刚' } else if (diff < 3600) { // less 1 hour return Math.ceil(diff / 60) + '分钟前' } else if (diff < 3600 * 24) { return Math.ceil(diff / 3600) + '小时前' } else if (diff < 3600 * 24 * 2) { return '1天前' } if (option) { return parseTime(time, option) } else { return ( d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' ) } } /** * @param {string} url * @returns {Object} */ export function getQueryObject(url) { url = url == null ? window.location.href : url const search = url.substring(url.lastIndexOf('?') + 1) const obj = {} const reg = /([^?&=]+)=([^?&=]*)/g search.replace(reg, (rs, $1, $2) => { const name = decodeURIComponent($1) let val = decodeURIComponent($2) val = String(val) obj[name] = val return rs }) return obj } /** * @param {string} input value * @returns {number} output value */ export function byteLength(str) { // returns the byte length of an utf8 string let s = str.length for (var i = str.length - 1; i >= 0; i--) { const code = str.charCodeAt(i) if (code > 0x7f && code <= 0x7ff) s++ else if (code > 0x7ff && code <= 0xffff) s += 2 if (code >= 0xDC00 && code <= 0xDFFF) i-- } return s } /** * @param {Array} actual * @returns {Array} */ export function cleanArray(actual) { const newArray = [] for (let i = 0; i < actual.length; i++) { if (actual[i]) { newArray.push(actual[i]) } } return newArray } /** * @param {Object} json * @returns {Array} */ export function param(json) { if (!json) return '' return cleanArray( Object.keys(json).map(key => { if (json[key] === undefined) return '' return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) }) ).join('&') } /** * @param {string} url * @returns {Object} */ export function param2Obj(url) { const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') if (!search) { return {} } const obj = {} const searchArr = search.split('&') searchArr.forEach(v => { const index = v.indexOf('=') if (index !== -1) { const name = v.substring(0, index) const val = v.substring(index + 1, v.length) obj[name] = val } }) return obj } /** * @param {string} val * @returns {string} */ export function html2Text(val) { const div = document.createElement('div') div.innerHTML = val return div.textContent || div.innerText } /** * Merges two objects, giving the last one precedence * @param {Object} target * @param {(Object|Array)} source * @returns {Object} */ export function objectMerge(target, source) { if (typeof target !== 'object') { target = {} } if (Array.isArray(source)) { return source.slice() } Object.keys(source).forEach(property => { const sourceProperty = source[property] if (typeof sourceProperty === 'object') { target[property] = objectMerge(target[property], sourceProperty) } else { target[property] = sourceProperty } }) return target } /** * @param {HTMLElement} element * @param {string} className */ export function toggleClass(element, className) { if (!element || !className) { return } let classString = element.className const nameIndex = classString.indexOf(className) if (nameIndex === -1) { classString += '' + className } else { classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length) } element.className = classString } /** * @param {string} type * @returns {Date} */ export function getTime(type) { if (type === 'start') { return new Date().getTime() - 3600 * 1000 * 24 * 90 } else { return new Date(new Date().toDateString()) } } /** * @param {Function} func * @param {number} wait * @param {boolean} immediate * @return {*} */ export function debounce(func, wait, immediate) { let timeout, args, context, timestamp, result const later = function() { // 据上一次触发时间间隔 const last = +new Date() - timestamp // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait if (last < wait && last > 0) { timeout = setTimeout(later, wait - last) } else { timeout = null // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 if (!immediate) { result = func.apply(context, args) if (!timeout) context = args = null } } } return function(...args) { context = this timestamp = +new Date() const callNow = immediate && !timeout // 如果延时不存在,重新设定延时 if (!timeout) timeout = setTimeout(later, wait) if (callNow) { result = func.apply(context, args) context = args = null } return result } } /** * This is just a simple version of deep copy * Has a lot of edge cases bug * If you want to use a perfect deep copy, use lodash's _.cloneDeep * @param {Object} source * @returns {Object} */ export function deepClone(source) { if (!source && typeof source !== 'object') { throw new Error('error arguments', 'deepClone') } const targetObj = source.constructor === Array ? [] : {} Object.keys(source).forEach(keys => { if (source[keys] && typeof source[keys] === 'object') { targetObj[keys] = deepClone(source[keys]) } else { targetObj[keys] = source[keys] } }) return targetObj } /** * @param {Array} arr * @returns {Array} */ export function uniqueArr(arr) { return Array.from(new Set(arr)) } /** * @returns {string} */ export function createUniqueString() { const timestamp = +new Date() + '' const randomNum = parseInt((1 + Math.random()) * 65536) + '' return (+(randomNum + timestamp)).toString(32) } /** * Check if an element has a class * @param {HTMLElement} elm * @param {string} cls * @returns {boolean} */ export function hasClass(ele, cls) { return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) } /** * Add class to element * @param {HTMLElement} elm * @param {string} cls */ export function addClass(ele, cls) { if (!hasClass(ele, cls)) ele.className += ' ' + cls } /** * Remove class from element * @param {HTMLElement} elm * @param {string} cls */ export function removeClass(ele, cls) { if (hasClass(ele, cls)) { const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') ele.className = ele.className.replace(reg, ' ') } } export function makeMap(str, expectsLowerCase) { const map = Object.create(null) const list = str.split(',') for (let i = 0; i < list.length; i++) { map[list[i]] = true } return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val] } export const exportDefault = 'export default ' export const beautifierConf = { html: { indent_size: '2', indent_char: ' ', max_preserve_newlines: '-1', preserve_newlines: false, keep_array_indentation: false, break_chained_methods: false, indent_scripts: 'separate', brace_style: 'end-expand', space_before_conditional: true, unescape_strings: false, jslint_happy: false, end_with_newline: true, wrap_line_length: '110', indent_inner_html: true, comma_first: false, e4x: true, indent_empty_lines: true }, js: { indent_size: '2', indent_char: ' ', max_preserve_newlines: '-1', preserve_newlines: false, keep_array_indentation: false, break_chained_methods: false, indent_scripts: 'normal', brace_style: 'end-expand', space_before_conditional: true, unescape_strings: false, jslint_happy: true, end_with_newline: true, wrap_line_length: '110', indent_inner_html: true, comma_first: false, e4x: true, indent_empty_lines: true } } // 首字母大小 export function titleCase(str) { return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) } // 下划转驼峰 export function camelCase(str) { return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) } export function isNumberStr(str) { return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) } ================================================ FILE: ruoyi-ui/src/utils/jsencrypt.js ================================================ import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' // 密钥对生成 http://web.chacuo.net/netrsakeypair const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + 'UP8iWi1Qw0Y=' // 加密 export function encrypt(txt) { const encryptor = new JSEncrypt() encryptor.setPublicKey(publicKey) // 设置公钥 return encryptor.encrypt(txt) // 对数据进行加密 } // 解密 export function decrypt(txt) { const encryptor = new JSEncrypt() encryptor.setPrivateKey(privateKey) // 设置私钥 return encryptor.decrypt(txt) // 对数据进行解密 } ================================================ FILE: ruoyi-ui/src/utils/permission.js ================================================ import store from '@/store' /** * 字符权限校验 * @param {Array} value 校验值 * @returns {Boolean} */ export function checkPermi(value) { if (value && value instanceof Array && value.length > 0) { const permissions = store.getters && store.getters.permissions const permissionDatas = value const all_permission = "*:*:*"; const hasPermission = permissions.some(permission => { return all_permission === permission || permissionDatas.includes(permission) }) if (!hasPermission) { return false } return true } else { console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`) return false } } /** * 角色权限校验 * @param {Array} value 校验值 * @returns {Boolean} */ export function checkRole(value) { if (value && value instanceof Array && value.length > 0) { const roles = store.getters && store.getters.roles const permissionRoles = value const super_admin = "admin"; const hasRole = roles.some(role => { return super_admin === role || permissionRoles.includes(role) }) if (!hasRole) { return false } return true } else { console.error(`need roles! Like checkRole="['admin','editor']"`) return false } } ================================================ FILE: ruoyi-ui/src/utils/request.js ================================================ import axios from 'axios' import { Notification, MessageBox, Message, Loading } from 'element-ui' import store from '@/store' import { getToken } from '@/utils/auth' import errorCode from '@/utils/errorCode' import { tansParams, blobValidate } from "@/utils/ruoyi"; import cache from '@/plugins/cache' import { saveAs } from 'file-saver' let downloadLoadingInstance; // 是否显示重新登录 export let isRelogin = { show: false }; axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' // 对应国际化资源文件后缀 axios.defaults.headers['Content-Language'] = 'zh_CN' // 创建axios实例 const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: process.env.VUE_APP_BASE_API, // 超时 timeout: 10000 }) // request拦截器 service.interceptors.request.use(config => { // 是否需要设置 token const isToken = (config.headers || {}).isToken === false // 是否需要防止数据重复提交 const isRepeatSubmit = (config.headers || {}).repeatSubmit === false if (getToken() && !isToken) { config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 } // get请求映射params参数 if (config.method === 'get' && config.params) { let url = config.url + '?' + tansParams(config.params); url = url.slice(0, -1); config.params = {}; config.url = url; } if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { const requestObj = { url: config.url, data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, time: new Date().getTime() } const sessionObj = cache.session.getJSON('sessionObj') if (sessionObj === undefined || sessionObj === null || sessionObj === '') { cache.session.setJSON('sessionObj', requestObj) } else { const s_url = sessionObj.url; // 请求地址 const s_data = sessionObj.data; // 请求数据 const s_time = sessionObj.time; // 请求时间 const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交 if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { const message = '数据正在处理,请勿重复提交'; console.warn(`[${s_url}]: ` + message) return Promise.reject(new Error(message)) } else { cache.session.setJSON('sessionObj', requestObj) } } } return config }, error => { console.log(error) Promise.reject(error) }) // 响应拦截器 service.interceptors.response.use(res => { // 未设置状态码则默认成功状态 const code = res.data.code || 200; // 获取错误信息 const msg = errorCode[code] || res.data.msg || errorCode['default'] // 二进制数据则直接返回 if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { return res.data } if (code === 401) { if (!isRelogin.show) { isRelogin.show = true; MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { isRelogin.show = false; store.dispatch('LogOut').then(() => { location.href = process.env.VUE_APP_CONTEXT_PATH + "index"; }) }).catch(() => { isRelogin.show = false; }); } return Promise.reject('无效的会话,或者会话已过期,请重新登录。') } else if (code === 500) { Message({ message: msg, type: 'error' }) return Promise.reject(new Error(msg)) } else if (code === 601) { Message({ message: msg, type: 'warning' }) return Promise.reject('error') } else if (code !== 200) { Notification.error({ title: msg }) return Promise.reject('error') } else { return res.data } }, error => { console.log('err' + error) let { message } = error; if (message == "Network Error") { message = "后端接口连接异常"; Message({ message: message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } else if (message.includes("timeout")) { message = "系统接口请求超时"; Message({ message: message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) } else if (message.includes("Request failed with status code")) { message = "系统接口" + message.substr(message.length - 3) + "异常"; //TODO 这种情况先不处理 } } ) // 通用下载方法 export function download(url, params, filename, config) { downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) return service.post(url, params, { transformRequest: [(params) => { return tansParams(params) }], headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, responseType: 'blob', ...config }).then(async (data) => { const isBlob = blobValidate(data); if (isBlob) { const blob = new Blob([data]) saveAs(blob, filename) } else { const resText = await data.text(); const rspObj = JSON.parse(resText); const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] Message.error(errMsg); } downloadLoadingInstance.close(); }).catch((r) => { console.error(r) Message.error('下载文件出现错误,请联系管理员!') downloadLoadingInstance.close(); }) } export default service ================================================ FILE: ruoyi-ui/src/utils/ruoyi.js ================================================ /** * 通用js方法封装处理 * Copyright (c) 2019 ruoyi */ // 日期格式化 export function parseTime(time, pattern) { if (arguments.length === 0 || !time) { return null } const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' let date if (typeof time === 'object') { date = time } else { if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { time = parseInt(time) } else if (typeof time === 'string') { time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), ''); } if ((typeof time === 'number') && (time.toString().length === 10)) { time = time * 1000 } date = new Date(time) } const formatObj = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate(), h: date.getHours(), i: date.getMinutes(), s: date.getSeconds(), a: date.getDay() } const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { let value = formatObj[key] // Note: getDay() returns 0 on Sunday if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } if (result.length > 0 && value < 10) { value = '0' + value } return value || 0 }) return time_str } // 表单重置 export function resetForm(refName) { if (this.$refs[refName]) { this.$refs[refName].resetFields(); } } // 添加日期范围 export function addDateRange(params, dateRange, propName) { let search = params; search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}; dateRange = Array.isArray(dateRange) ? dateRange : []; if (typeof (propName) === 'undefined') { search.params['beginTime'] = dateRange[0]; search.params['endTime'] = dateRange[1]; } else { search.params['begin' + propName] = dateRange[0]; search.params['end' + propName] = dateRange[1]; } return search; } // 回显数据字典 export function selectDictLabel(datas, value) { if (value === undefined) { return ""; } var actions = []; Object.keys(datas).some((key) => { if (datas[key].value == ('' + value)) { actions.push(datas[key].label); return true; } }) if (actions.length === 0) { actions.push(value); } return actions.join(''); } // 回显数据字典(字符串、数组) export function selectDictLabels(datas, value, separator) { if (value === undefined || value.length ===0) { return ""; } if (Array.isArray(value)) { value = value.join(","); } var actions = []; var currentSeparator = undefined === separator ? "," : separator; var temp = value.split(currentSeparator); Object.keys(value.split(currentSeparator)).some((val) => { var match = false; Object.keys(datas).some((key) => { if (datas[key].value == ('' + temp[val])) { actions.push(datas[key].label + currentSeparator); match = true; } }) if (!match) { actions.push(temp[val] + currentSeparator); } }) return actions.join('').substring(0, actions.join('').length - 1); } // 字符串格式化(%s ) export function sprintf(str) { var args = arguments, flag = true, i = 1; str = str.replace(/%s/g, function () { var arg = args[i++]; if (typeof arg === 'undefined') { flag = false; return ''; } return arg; }); return flag ? str : ''; } // 转换字符串,undefined,null等转化为"" export function parseStrEmpty(str) { if (!str || str == "undefined" || str == "null") { return ""; } return str; } // 数据合并 export function mergeRecursive(source, target) { for (var p in target) { try { if (target[p].constructor == Object) { source[p] = mergeRecursive(source[p], target[p]); } else { source[p] = target[p]; } } catch (e) { source[p] = target[p]; } } return source; }; /** * 构造树型结构数据 * @param {*} data 数据源 * @param {*} id id字段 默认 'id' * @param {*} parentId 父节点字段 默认 'parentId' * @param {*} children 孩子节点字段 默认 'children' */ export function handleTree(data, id, parentId, children) { let config = { id: id || 'id', parentId: parentId || 'parentId', childrenList: children || 'children' }; var childrenListMap = {}; var nodeIds = {}; var tree = []; for (let d of data) { let parentId = d[config.parentId]; if (childrenListMap[parentId] == null) { childrenListMap[parentId] = []; } nodeIds[d[config.id]] = d; childrenListMap[parentId].push(d); } for (let d of data) { let parentId = d[config.parentId]; if (nodeIds[parentId] == null) { tree.push(d); } } for (let t of tree) { adaptToChildrenList(t); } function adaptToChildrenList(o) { if (childrenListMap[o[config.id]] !== null) { o[config.childrenList] = childrenListMap[o[config.id]]; } if (o[config.childrenList]) { for (let c of o[config.childrenList]) { adaptToChildrenList(c); } } } return tree; } /** * 参数处理 * @param {*} params 参数 */ export function tansParams(params) { let result = '' for (const propName of Object.keys(params)) { const value = params[propName]; var part = encodeURIComponent(propName) + "="; if (value !== null && value !== "" && typeof (value) !== "undefined") { if (typeof value === 'object') { for (const key of Object.keys(value)) { if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { let params = propName + '[' + key + ']'; var subPart = encodeURIComponent(params) + "="; result += subPart + encodeURIComponent(value[key]) + "&"; } } } else { result += part + encodeURIComponent(value) + "&"; } } } return result } // 验证是否为blob格式 export function blobValidate(data) { return data.type !== 'application/json' } ================================================ FILE: ruoyi-ui/src/utils/scroll-to.js ================================================ Math.easeInOutQuad = function(t, b, c, d) { t /= d / 2 if (t < 1) { return c / 2 * t * t + b } t-- return -c / 2 * (t * (t - 2) - 1) + b } // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts var requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } })() /** * Because it's so fucking difficult to detect the scrolling element, just move them all * @param {number} amount */ function move(amount) { document.documentElement.scrollTop = amount document.body.parentNode.scrollTop = amount document.body.scrollTop = amount } function position() { return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop } /** * @param {number} to * @param {number} duration * @param {Function} callback */ export function scrollTo(to, duration, callback) { const start = position() const change = to - start const increment = 20 let currentTime = 0 duration = (typeof (duration) === 'undefined') ? 500 : duration var animateScroll = function() { // increment the time currentTime += increment // find the value with the quadratic in-out easing function var val = Math.easeInOutQuad(currentTime, start, change, duration) // move the document.body move(val) // do the animation unless its over if (currentTime < duration) { requestAnimFrame(animateScroll) } else { if (callback && typeof (callback) === 'function') { // the animation is done so lets callback callback() } } } animateScroll() } ================================================ FILE: ruoyi-ui/src/utils/validate.js ================================================ /** * @param {string} path * @returns {Boolean} */ export function isExternal(path) { return /^(https?:|mailto:|tel:)/.test(path) } /** * @param {string} str * @returns {Boolean} */ export function validUsername(str) { const valid_map = ['admin', 'editor'] return valid_map.indexOf(str.trim()) >= 0 } /** * @param {string} url * @returns {Boolean} */ export function validURL(url) { const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ return reg.test(url) } /** * @param {string} str * @returns {Boolean} */ export function validLowerCase(str) { const reg = /^[a-z]+$/ return reg.test(str) } /** * @param {string} str * @returns {Boolean} */ export function validUpperCase(str) { const reg = /^[A-Z]+$/ return reg.test(str) } /** * @param {string} str * @returns {Boolean} */ export function validAlphabets(str) { const reg = /^[A-Za-z]+$/ return reg.test(str) } /** * @param {string} email * @returns {Boolean} */ export function validEmail(email) { const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ return reg.test(email) } /** * @param {string} str * @returns {Boolean} */ export function isString(str) { if (typeof str === 'string' || str instanceof String) { return true } return false } /** * @param {Array} arg * @returns {Boolean} */ export function isArray(arg) { if (typeof Array.isArray === 'undefined') { return Object.prototype.toString.call(arg) === '[object Array]' } return Array.isArray(arg) } ================================================ FILE: ruoyi-ui/src/views/components/icons/element-icons.js ================================================ const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round'] export default elementIcons ================================================ FILE: ruoyi-ui/src/views/components/icons/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/components/icons/svg-icons.js ================================================ const req = require.context('../../../assets/icons/svg', false, /\.svg$/) const requireAll = requireContext => requireContext.keys() const re = /\.\/(.*)\.svg/ const svgIcons = requireAll(req).map(i => { return i.match(re)[1] }) export default svgIcons ================================================ FILE: ruoyi-ui/src/views/dashboard/BarChart.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/dashboard/LineChart.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/dashboard/PanelGroup.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/dashboard/PieChart.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/dashboard/RaddarChart.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/dashboard/mixins/resize.js ================================================ import { debounce } from '@/utils' export default { data() { return { $_sidebarElm: null, $_resizeHandler: null } }, mounted() { this.initListener() }, activated() { if (!this.$_resizeHandler) { // avoid duplication init this.initListener() } // when keep-alive chart activated, auto resize this.resize() }, beforeDestroy() { this.destroyListener() }, deactivated() { this.destroyListener() }, methods: { // use $_ for mixins properties // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential $_sidebarResizeHandler(e) { if (e.propertyName === 'width') { this.$_resizeHandler() } }, initListener() { this.$_resizeHandler = debounce(() => { this.resize() }, 100) window.addEventListener('resize', this.$_resizeHandler) this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) }, destroyListener() { window.removeEventListener('resize', this.$_resizeHandler) this.$_resizeHandler = null this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) }, resize() { const { chart } = this chart && chart.resize() } } } ================================================ FILE: ruoyi-ui/src/views/demo/demo/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/demo/tree/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/error/401.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/error/404.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/index_v1.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/login.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/monitor/admin/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/monitor/cache/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/monitor/cache/list.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/monitor/logininfor/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/monitor/online/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/monitor/operlog/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/monitor/xxljob/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/redirect.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/register.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/activity/add/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/activity/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/activityConnArtist/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/activityConnIntro/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/activityConnTag/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/activityGroup/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/activityGroupApply/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/artist/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/config/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/dept/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/dict/data.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/dict/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/intro/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/menu/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/notice/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/official/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/organizer/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/organizerTicket/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/oss/config.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/oss/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/post/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/pzc_order/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/pzc_user/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/pzc_user/todoList/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/region/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/role/authUser.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/role/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/role/selectUser.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/tag/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/user/authRole.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/user/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/user/profile/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/user/profile/resetPwd.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/user/profile/userAvatar.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/user/profile/userInfo.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/userCollect/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/userHistory/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/userPhoto/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/userTalk/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/system/viewPager/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/build/DraggableItem.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/build/IconsDialog.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/build/RightPanel.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/build/index.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/gen/basicInfoForm.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/gen/editTable.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/gen/genInfoForm.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/gen/importTable.vue ================================================ ================================================ FILE: ruoyi-ui/src/views/tool/gen/index.vue ================================================ ================================================ FILE: ruoyi-ui/vue.config.js ================================================ 'use strict' const path = require('path') function resolve(dir) { return path.join(__dirname, dir) } const CompressionPlugin = require('compression-webpack-plugin') const name = process.env.VUE_APP_TITLE || '派之城后台管理系统' // 网页标题 const port = process.env.port || process.env.npm_config_port || 80 // 端口 // vue.config.js 配置说明 //官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions // 这里只列一部分,具体配置参考文档 module.exports = { // 部署生产环境和开发环境下的URL。 // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上 // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 publicPath: process.env.VUE_APP_CONTEXT_PATH, // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist) outputDir: 'dist', // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下) assetsDir: 'static', // 是否开启eslint保存检测,有效值:ture | false | 'error' lintOnSave: false, //process.env.NODE_ENV === 'development', // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。 productionSourceMap: false, // webpack-dev-server 相关配置 devServer: { host: '0.0.0.0', port: port, open: true, proxy: { // detail: https://cli.vuejs.org/config/#devserver-proxy [process.env.VUE_APP_BASE_API]: { target: `http://localhost:9393`, //`https://flya.mynatapp.cc`, changeOrigin: true, pathRewrite: { ['^' + process.env.VUE_APP_BASE_API]: '' } } }, disableHostCheck: true }, css: { loaderOptions: { sass: { sassOptions: { outputStyle: "expanded" } } } }, configureWebpack: { name: name, resolve: { alias: { '@': resolve('src') } }, plugins: [ // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件 new CompressionPlugin({ cache: false, // 不启用文件缓存 test: /\.(js|css|html)?$/i, // 压缩文件格式 filename: '[path].gz[query]', // 压缩后的文件名 algorithm: 'gzip', // 使用gzip压缩 minRatio: 0.8 // 压缩率小于1才会压缩 }) ], }, chainWebpack(config) { config.plugins.delete('preload') // TODO: need test config.plugins.delete('prefetch') // TODO: need test // set svg-sprite-loader config.module .rule('svg') .exclude.add(resolve('src/assets/icons')) .end() config.module .rule('icons') .test(/\.svg$/) .include.add(resolve('src/assets/icons')) .end() .use('svg-sprite-loader') .loader('svg-sprite-loader') .options({ symbolId: 'icon-[name]' }) .end() config .when(process.env.NODE_ENV !== 'development', config => { config .plugin('ScriptExtHtmlWebpackPlugin') .after('html') .use('script-ext-html-webpack-plugin', [{ // `runtime` must same as runtimeChunk name. default is `runtime` inline: /runtime\..*\.js$/ }]) .end() config .optimization.splitChunks({ chunks: 'all', cacheGroups: { libs: { name: 'chunk-libs', test: /[\\/]node_modules[\\/]/, priority: 10, chunks: 'initial' // only package third parties that are initially dependent }, elementUI: { name: 'chunk-elementUI', // split elementUI into a single package priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm }, commons: { name: 'chunk-commons', test: resolve('src/components'), // can customize your rules minChunks: 3, // minimum common number priority: 5, reuseExistingChunk: true } } }) config.optimization.runtimeChunk('single'), { from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件 to: './' //到根目录下 } } ) } } ================================================ FILE: script/bin/ry.bat ================================================ rem 使用者应根据自身平台编码自行转换 防止乱码 例如 win使用gbk编码 @echo off rem jar平级目录 set AppName=ruoyi-admin.jar rem JVM参数 set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" ECHO. ECHO. [1] 启动%AppName% ECHO. [2] 关闭%AppName% ECHO. [3] 重启%AppName% ECHO. [4] 启动状态 %AppName% ECHO. [5] 退 出 ECHO. ECHO.请输入选择项目的序号: set /p ID= IF "%id%"=="1" GOTO start IF "%id%"=="2" GOTO stop IF "%id%"=="3" GOTO restart IF "%id%"=="4" GOTO status IF "%id%"=="5" EXIT PAUSE :start for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( set pid=%%a set image_name=%%b ) if defined pid ( echo %%is running PAUSE ) start javaw %JVM_OPTS% -jar %AppName% echo starting…… echo Start %AppName% success... goto:eof rem 函数stop通过jps命令查找pid并结束进程 :stop for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( set pid=%%a set image_name=%%b ) if not defined pid (echo process %AppName% does not exists) else ( echo prepare to kill %image_name% echo start kill %pid% ... rem 根据进程ID,kill进程 taskkill /f /pid %pid% ) goto:eof :restart call :stop call :start goto:eof :status for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( set pid=%%a set image_name=%%b ) if not defined pid (echo process %AppName% is dead ) else ( echo %image_name% is running ) goto:eof ================================================ FILE: script/bin/ry.sh ================================================ #!/bin/sh # ./ry.sh start 启动 stop 停止 restart 重启 status 状态 -Dserver.port=9393 AppName=ruoyi-admin.jar # JVM参数 JVM_OPTS=" -Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" APP_HOME=`pwd` LOG_PATH=$APP_HOME/logs/$AppName.log if [ "$1" = "" ]; then echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" exit 1 fi if [ "$AppName" = "" ]; then echo -e "\033[0;31m 未输入应用名 \033[0m" exit 1 fi function start() { PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` if [ x"$PID" != x"" ]; then echo "$AppName is running..." else nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & echo "Start $AppName success..." fi } function stop() { echo "Stop $AppName" PID="" query(){ PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` } query if [ x"$PID" != x"" ]; then kill -TERM $PID echo "$AppName (pid:$PID) exiting..." while [ x"$PID" != x"" ] do sleep 1 query done echo "$AppName exited." else echo "$AppName already stopped." fi } function restart() { stop sleep 2 start } function status() { PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` if [ $PID != 0 ];then echo "$AppName is running..." else echo "$AppName is not running..." fi } case $1 in start) start;; stop) stop;; restart) restart;; status) status;; *) esac ================================================ FILE: script/docker/database.yml ================================================ version: '3' services: # 此镜像仅用于测试 正式环境需自行安装数据库 # SID: XE user: system password: oracle oracle: image: tekintian/oracle12c:latest container_name: oracle environment: # 时区上海 TZ: Asia/Shanghai DBCA_TOTAL_MEMORY: 16192 ports: - "18080:8080" - "1521:1521" volumes: # 数据挂载 - "/docker/oracle/data:/u01/app/oracle" network_mode: "host" # 此镜像仅用于测试 正式环境需自行安装数据库 sqlserver: image: mcr.microsoft.com/mssql/server:2017-latest container_name: sqlserver environment: # 时区上海 TZ: Asia/Shanghai ACCEPT_EULA: "Y" SA_PASSWORD: "Ruoyi@123" ports: - "1433:1433" volumes: # 数据挂载 - "/docker/sqlserver/data:/var/opt/mssql" network_mode: "host" postgres: image: postgres:14.2 container_name: postgres environment: POSTGRES_USER: root POSTGRES_PASSWORD: root POSTGRES_DB: postgres ports: - "5432:5432" volumes: - /docker/postgres/data:/var/lib/postgresql/data network_mode: "host" postgres13: image: postgres:13.6 container_name: postgres13 environment: POSTGRES_USER: root POSTGRES_PASSWORD: root POSTGRES_DB: postgres ports: - "5433:5432" volumes: - /docker/postgres13/data:/var/lib/postgresql/data network_mode: "host" ================================================ FILE: script/docker/docker-compose.yml ================================================ version: '3' services: nginx-web: image: nginx:1.22.1 container_name: nginx-web environment: # 时区上海 TZ: Asia/Shanghai ports: - "80:80" - "443:443" volumes: # 证书映射 - /mnt/hiwoo/nginx/cert:/etc/nginx/cert # 配置文件映射 - /mnt/hiwoo/nginx/conf/nginx.conf:/etc/nginx/nginx.conf # 页面目录 - /mnt/hiwoo/nginx/html:/usr/share/nginx/html # 日志目录 - /mnt/hiwoo/nginx/logs:/var/log/nginx privileged: true network_mode: "host" ================================================ FILE: script/docker/nginx/conf/nginx.conf ================================================ worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; # 限制body大小 client_max_body_size 100m; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; upstream server { ip_hash; server 127.0.0.1:8080; server 127.0.0.1:8081; } upstream monitor-admin { server 127.0.0.1:9090; } upstream xxljob-admin { server 127.0.0.1:9100; } server { listen 80; server_name localhost; # https配置参考 start #listen 443 ssl; # 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径 #ssl on; #ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改 #ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改 #ssl_session_timeout 5m; #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_prefer_server_ciphers on; # https配置参考 end # 演示环境配置 拦截除 GET POST 之外的所有请求 # if ($request_method !~* GET|POST) { # rewrite ^/(.*)$ /403; # } # location = /403 { # default_type application/json; # return 200 '{"msg":"演示模式,不允许操作","code":500}'; # } # 限制外网访问内网 actuator 相关路径 location ~ ^(/[^/]*)?/actuator(/.*)?$ { return 403; } location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; index index.html index.htm; } location /prod-api/ { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://server/; } # https 会拦截内链所有的 http 请求 造成功能无法使用 # 解决方案1 将 admin 服务 也配置成 https # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问 location /admin/ { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://monitor-admin/admin/; } # https 会拦截内链所有的 http 请求 造成功能无法使用 # 解决方案1 将 xxljob 服务 也配置成 https # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问 location /xxl-job-admin/ { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://xxljob-admin/xxl-job-admin/; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } ================================================ FILE: script/docker/redis/conf/redis.conf ================================================ # redis 密码 requirepass ruoyi123 # key 监听器配置 # notify-keyspace-events Ex # 配置持久化文件存储路径 dir /redis/data # 配置rdb # 15分钟内有至少1个key被更改则进行快照 save 900 1 # 5分钟内有至少10个key被更改则进行快照 save 300 10 # 1分钟内有至少10000个key被更改则进行快照 save 60 10000 # 开启压缩 rdbcompression yes # rdb文件名 用默认的即可 dbfilename dump.rdb # 开启aof appendonly yes # 文件名 appendfilename "appendonly.aof" # 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢 # appendfsync always appendfsync everysec # appendfsync no ================================================ FILE: script/docker/redis/data/README.md ================================================ 数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据 ================================================ FILE: script/sql/oracle/oracle_ry_vue_4.X.sql ================================================ -- ---------------------------- -- 1、部门表 -- ---------------------------- create table sys_dept ( dept_id number(20) not null, parent_id number(20) default 0, ancestors varchar2(500) default '', dept_name varchar2(30) default '', order_num number(4) default 0, leader varchar2(20) default null, phone varchar2(11) default null, email varchar2(50) default null, status char(1) default '0', del_flag char(1) default '0', create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date ); alter table sys_dept add constraint pk_sys_dept primary key (dept_id); comment on table sys_dept is '部门表'; comment on column sys_dept.dept_id is '部门id'; comment on column sys_dept.parent_id is '父部门id'; comment on column sys_dept.ancestors is '祖级列表'; comment on column sys_dept.dept_name is '部门名称'; comment on column sys_dept.order_num is '显示顺序'; comment on column sys_dept.leader is '负责人'; comment on column sys_dept.phone is '联系电话'; comment on column sys_dept.email is '邮箱'; comment on column sys_dept.status is '部门状态(0正常 1停用)'; comment on column sys_dept.del_flag is '删除标志(0代表存在 2代表删除)'; comment on column sys_dept.create_by is '创建者'; comment on column sys_dept.create_time is '创建时间'; comment on column sys_dept.update_by is '更新者'; comment on column sys_dept.update_time is '更新时间'; -- ---------------------------- -- 初始化-部门表数据 -- ---------------------------- insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate, '', null); -- ---------------------------- -- 2、用户信息表 -- ---------------------------- create table sys_user ( user_id number(20) not null, dept_id number(20) default null, user_name varchar2(40) not null, nick_name varchar2(40) not null, user_type varchar2(10) default 'sys_user', email varchar2(50) default '', phonenumber varchar2(11) default '', sex char(1) default '0', avatar varchar2(100) default '', password varchar2(100) default '', status char(1) default '0', del_flag char(1) default '0', login_ip varchar2(128) default '', login_date date, create_by varchar2(64), create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(500) default '' ); alter table sys_user add constraint pk_sys_user primary key (user_id); comment on table sys_user is '用户信息表'; comment on column sys_user.user_id is '用户ID'; comment on column sys_user.dept_id is '部门ID'; comment on column sys_user.user_name is '用户账号'; comment on column sys_user.nick_name is '用户昵称'; comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; comment on column sys_user.email is '用户邮箱'; comment on column sys_user.phonenumber is '手机号码'; comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; comment on column sys_user.avatar is '头像路径'; comment on column sys_user.password is '密码'; comment on column sys_user.status is '帐号状态(0正常 1停用)'; comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)'; comment on column sys_user.login_ip is '最后登录IP'; comment on column sys_user.login_date is '最后登录时间'; comment on column sys_user.create_by is '创建者'; comment on column sys_user.create_time is '创建时间'; comment on column sys_user.update_by is '更新者'; comment on column sys_user.update_time is '更新时间'; comment on column sys_user.remark is '备注'; -- ---------------------------- -- 初始化-用户信息表数据 -- ---------------------------- insert into sys_user values(1, 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, '', null, '管理员'); insert into sys_user values(2, 105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, '', null, '测试员'); -- ---------------------------- -- 3、岗位信息表 -- ---------------------------- create table sys_post ( post_id number(20) not null, post_code varchar2(64) not null, post_name varchar2(50) not null, post_sort number(4) not null, status char(1) not null, create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(500) ); alter table sys_post add constraint pk_sys_post primary key (post_id); comment on table sys_post is '岗位信息表'; comment on column sys_post.post_id is '岗位ID'; comment on column sys_post.post_code is '岗位编码'; comment on column sys_post.post_name is '岗位名称'; comment on column sys_post.post_sort is '显示顺序'; comment on column sys_post.status is '状态(0正常 1停用)'; comment on column sys_post.create_by is '创建者'; comment on column sys_post.create_time is '创建时间'; comment on column sys_post.update_by is '更新者'; comment on column sys_post.update_time is '更新时间'; comment on column sys_post.remark is '备注'; -- ---------------------------- -- 初始化-岗位信息表数据 -- ---------------------------- insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate, '', null, ''); insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate, '', null, ''); insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate, '', null, ''); insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate, '', null, ''); -- ---------------------------- -- 4、角色信息表 -- ---------------------------- create table sys_role ( role_id number(20) not null, role_name varchar2(30) not null, role_key varchar2(100) not null, role_sort number(4) not null, data_scope char(1) default '1', menu_check_strictly number(1) default 1, dept_check_strictly number(1) default 1, status char(1) not null, del_flag char(1) default '0', create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(500) default null ); alter table sys_role add constraint pk_sys_role primary key (role_id); comment on table sys_role is '角色信息表'; comment on column sys_role.role_id is '角色ID'; comment on column sys_role.role_name is '角色名称'; comment on column sys_role.role_key is '角色权限字符串'; comment on column sys_role.role_sort is '显示顺序'; comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; comment on column sys_role.status is '角色状态(0正常 1停用)'; comment on column sys_role.del_flag is '删除标志(0代表存在 2代表删除)'; comment on column sys_role.create_by is '创建者'; comment on column sys_role.create_time is '创建时间'; comment on column sys_role.update_by is '更新者'; comment on column sys_role.update_time is '更新时间'; comment on column sys_role.remark is '备注'; -- ---------------------------- -- 初始化-角色信息表数据 -- ---------------------------- insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate, '', null, '超级管理员'); insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate, '', null, '普通角色'); -- ---------------------------- -- 5、菜单权限表 -- ---------------------------- create table sys_menu ( menu_id number(20) not null, menu_name varchar2(50) not null, parent_id number(20) default 0, order_num number(4) default 0, path varchar(200) default '', component varchar(255) default null, query_param varchar(255) default null, is_frame number(1) default 1, is_cache number(1) default 0, menu_type char(1) default '', visible char(1) default 0, status char(1) default 0, perms varchar2(100) default null, icon varchar2(100) default '#', create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date , remark varchar2(500) default '' ); alter table sys_menu add constraint pk_sys_menu primary key (menu_id); comment on table sys_menu is '菜单权限表'; comment on column sys_menu.menu_id is '菜单ID'; comment on column sys_menu.menu_name is '菜单名称'; comment on column sys_menu.parent_id is '父菜单ID'; comment on column sys_menu.order_num is '显示顺序'; comment on column sys_menu.path is '请求地址'; comment on column sys_menu.component is '路由地址'; comment on column sys_menu.query_param is '路由参数'; comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; comment on column sys_menu.status is '菜单状态(0正常 1停用)'; comment on column sys_menu.perms is '权限标识'; comment on column sys_menu.icon is '菜单图标'; comment on column sys_menu.create_by is '创建者'; comment on column sys_menu.create_time is '创建时间'; comment on column sys_menu.update_by is '更新者'; comment on column sys_menu.update_time is '更新时间'; comment on column sys_menu.remark is '备注'; -- ---------------------------- -- 初始化-菜单信息表数据 -- ---------------------------- -- 一级菜单 insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate, '', null, '系统管理目录'); insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate, '', null, '系统监控目录'); insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate, '', null, '系统工具目录'); insert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate, '', null, 'RuoYi-Vue-Plus官网地址'); -- 二级菜单 insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate, '', null, '用户管理菜单'); insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate, '', null, '角色管理菜单'); insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate, '', null, '菜单管理菜单'); insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate, '', null, '部门管理菜单'); insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate, '', null, '岗位管理菜单'); insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate, '', null, '字典管理菜单'); insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate, '', null, '参数设置菜单'); insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate, '', null, '通知公告菜单'); insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate, '', null, '日志管理菜单'); insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate, '', null, '在线用户菜单'); insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate, '', null, '缓存列表菜单'); insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate, '', null, '缓存监控菜单'); insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate, '', null, '表单构建菜单'); insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate, '', null, '代码生成菜单'); -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 'admin', sysdate, '', null, 'Admin监控菜单'); -- oss菜单 insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 'admin', sysdate, '', null, '文件管理菜单'); -- xxl-job-admin控制台 insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', 1, 0, 'C', '0', '0', 'monitor:xxljob:list', 'job', 'admin', sysdate, '', null, 'Xxl-Job控制台菜单'); -- 三级菜单 insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate, '', null, '操作日志菜单'); insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate, '', null, '登录日志菜单'); -- 用户管理按钮 insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate, '', null, ''); -- 角色管理按钮 insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate, '', null, ''); -- 菜单管理按钮 insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate, '', null, ''); -- 部门管理按钮 insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate, '', null, ''); -- 岗位管理按钮 insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate, '', null, ''); -- 字典管理按钮 insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate, '', null, ''); -- 参数设置按钮 insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate, '', null, ''); -- 通知公告按钮 insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate, '', null, ''); -- 操作日志按钮 insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate, '', null, ''); -- 登录日志按钮 insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate, '', null, ''); -- 在线用户按钮 insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate, '', null, ''); -- 代码生成按钮 insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate, '', null, ''); -- oss相关按钮 insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit', '#', 'admin', sysdate, '', null, ''); -- ---------------------------- -- 6、用户和角色关联表 用户N-1角色 -- ---------------------------- create table sys_user_role ( user_id number(20) not null, role_id number(20) not null ); alter table sys_user_role add constraint pk_sys_user_role primary key (user_id, role_id); comment on table sys_user_role is '用户和角色关联表'; comment on column sys_user_role.user_id is '用户ID'; comment on column sys_user_role.role_id is '角色ID'; -- ---------------------------- -- 初始化-用户和角色关联表数据 -- ---------------------------- insert into sys_user_role values ('1', '1'); insert into sys_user_role values ('2', '2'); -- ---------------------------- -- 7、角色和菜单关联表 角色1-N菜单 -- ---------------------------- create table sys_role_menu ( role_id number(20) not null, menu_id number(20) not null ); alter table sys_role_menu add constraint pk_sys_role_menu primary key (role_id, menu_id); comment on table sys_role_menu is '角色和菜单关联表'; comment on column sys_role_menu.role_id is '角色ID'; comment on column sys_role_menu.menu_id is '菜单ID'; -- ---------------------------- -- 初始化-角色和菜单关联表数据 -- ---------------------------- insert into sys_role_menu values ('2', '1'); insert into sys_role_menu values ('2', '2'); insert into sys_role_menu values ('2', '3'); insert into sys_role_menu values ('2', '4'); insert into sys_role_menu values ('2', '100'); insert into sys_role_menu values ('2', '101'); insert into sys_role_menu values ('2', '102'); insert into sys_role_menu values ('2', '103'); insert into sys_role_menu values ('2', '104'); insert into sys_role_menu values ('2', '105'); insert into sys_role_menu values ('2', '106'); insert into sys_role_menu values ('2', '107'); insert into sys_role_menu values ('2', '108'); insert into sys_role_menu values ('2', '109'); insert into sys_role_menu values ('2', '110'); insert into sys_role_menu values ('2', '111'); insert into sys_role_menu values ('2', '112'); insert into sys_role_menu values ('2', '113'); insert into sys_role_menu values ('2', '114'); insert into sys_role_menu values ('2', '115'); insert into sys_role_menu values ('2', '116'); insert into sys_role_menu values ('2', '500'); insert into sys_role_menu values ('2', '501'); insert into sys_role_menu values ('2', '1000'); insert into sys_role_menu values ('2', '1001'); insert into sys_role_menu values ('2', '1002'); insert into sys_role_menu values ('2', '1003'); insert into sys_role_menu values ('2', '1004'); insert into sys_role_menu values ('2', '1005'); insert into sys_role_menu values ('2', '1006'); insert into sys_role_menu values ('2', '1007'); insert into sys_role_menu values ('2', '1008'); insert into sys_role_menu values ('2', '1009'); insert into sys_role_menu values ('2', '1010'); insert into sys_role_menu values ('2', '1011'); insert into sys_role_menu values ('2', '1012'); insert into sys_role_menu values ('2', '1013'); insert into sys_role_menu values ('2', '1014'); insert into sys_role_menu values ('2', '1015'); insert into sys_role_menu values ('2', '1016'); insert into sys_role_menu values ('2', '1017'); insert into sys_role_menu values ('2', '1018'); insert into sys_role_menu values ('2', '1019'); insert into sys_role_menu values ('2', '1020'); insert into sys_role_menu values ('2', '1021'); insert into sys_role_menu values ('2', '1022'); insert into sys_role_menu values ('2', '1023'); insert into sys_role_menu values ('2', '1024'); insert into sys_role_menu values ('2', '1025'); insert into sys_role_menu values ('2', '1026'); insert into sys_role_menu values ('2', '1027'); insert into sys_role_menu values ('2', '1028'); insert into sys_role_menu values ('2', '1029'); insert into sys_role_menu values ('2', '1030'); insert into sys_role_menu values ('2', '1031'); insert into sys_role_menu values ('2', '1032'); insert into sys_role_menu values ('2', '1033'); insert into sys_role_menu values ('2', '1034'); insert into sys_role_menu values ('2', '1035'); insert into sys_role_menu values ('2', '1036'); insert into sys_role_menu values ('2', '1037'); insert into sys_role_menu values ('2', '1038'); insert into sys_role_menu values ('2', '1039'); insert into sys_role_menu values ('2', '1040'); insert into sys_role_menu values ('2', '1041'); insert into sys_role_menu values ('2', '1042'); insert into sys_role_menu values ('2', '1043'); insert into sys_role_menu values ('2', '1044'); insert into sys_role_menu values ('2', '1045'); insert into sys_role_menu values ('2', '1050'); insert into sys_role_menu values ('2', '1046'); insert into sys_role_menu values ('2', '1047'); insert into sys_role_menu values ('2', '1048'); insert into sys_role_menu values ('2', '1055'); insert into sys_role_menu values ('2', '1056'); insert into sys_role_menu values ('2', '1057'); insert into sys_role_menu values ('2', '1058'); insert into sys_role_menu values ('2', '1059'); insert into sys_role_menu values ('2', '1060'); -- ---------------------------- -- 8、角色和部门关联表 角色1-N部门 -- ---------------------------- create table sys_role_dept ( role_id number(20) not null, dept_id number(20) not null ); alter table sys_role_dept add constraint pk_sys_role_dept primary key (role_id, dept_id); comment on table sys_role_dept is '角色和部门关联表'; comment on column sys_role_dept.role_id is '角色ID'; comment on column sys_role_dept.dept_id is '部门ID'; -- ---------------------------- -- 初始化-角色和部门关联表数据 -- ---------------------------- insert into sys_role_dept values ('2', '100'); insert into sys_role_dept values ('2', '101'); insert into sys_role_dept values ('2', '105'); -- ---------------------------- -- 9、用户与岗位关联表 用户1-N岗位 -- ---------------------------- create table sys_user_post ( user_id number(20) not null, post_id number(20) not null ); alter table sys_user_post add constraint pk_sys_user_post primary key (user_id, post_id); comment on table sys_user_post is '用户与岗位关联表'; comment on column sys_user_post.user_id is '用户ID'; comment on column sys_user_post.post_id is '岗位ID'; -- ---------------------------- -- 初始化-用户与岗位关联表数据 -- ---------------------------- insert into sys_user_post values ('1', '1'); insert into sys_user_post values ('2', '2'); -- ---------------------------- -- 10、操作日志记录 -- ---------------------------- create table sys_oper_log ( oper_id number(20) not null , title varchar2(50) default '', business_type number(2) default 0, method varchar2(100) default '', request_method varchar(10) default '', operator_type number(1) default 0, oper_name varchar2(50) default '', dept_name varchar2(50) default '', oper_url varchar2(255) default '', oper_ip varchar2(128) default '', oper_location varchar2(255) default '', oper_param varchar2(2100) default '', json_result varchar2(2100) default '', status number(1) default 0, error_msg varchar2(2100) default '' , oper_time date ); alter table sys_oper_log add constraint pk_sys_oper_log primary key (oper_id); create index idx_sys_oper_log_bt on sys_oper_log (business_type); create index idx_sys_oper_log_s on sys_oper_log (status); create index idx_sys_oper_log_ot on sys_oper_log (oper_time); comment on table sys_oper_log is '操作日志记录'; comment on column sys_oper_log.oper_id is '日志主键'; comment on column sys_oper_log.title is '模块标题'; comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; comment on column sys_oper_log.method is '方法名称'; comment on column sys_oper_log.request_method is '请求方式'; comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; comment on column sys_oper_log.oper_name is '操作人员'; comment on column sys_oper_log.dept_name is '部门名称'; comment on column sys_oper_log.oper_url is '请求URL'; comment on column sys_oper_log.oper_ip is '主机地址'; comment on column sys_oper_log.oper_location is '操作地点'; comment on column sys_oper_log.oper_param is '请求参数'; comment on column sys_oper_log.json_result is '返回参数'; comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; comment on column sys_oper_log.error_msg is '错误消息'; comment on column sys_oper_log.oper_time is '操作时间'; -- ---------------------------- -- 11、字典类型表 -- ---------------------------- create table sys_dict_type ( dict_id number(20) not null, dict_name varchar2(100) default '', dict_type varchar2(100) default '', status char(1) default '0', create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(500) default null ); alter table sys_dict_type add constraint pk_sys_dict_type primary key (dict_id); create unique index sys_dict_type_index1 on sys_dict_type (dict_type); comment on table sys_dict_type is '字典类型表'; comment on column sys_dict_type.dict_id is '字典主键'; comment on column sys_dict_type.dict_name is '字典名称'; comment on column sys_dict_type.dict_type is '字典类型'; comment on column sys_dict_type.status is '状态(0正常 1停用)'; comment on column sys_dict_type.create_by is '创建者'; comment on column sys_dict_type.create_time is '创建时间'; comment on column sys_dict_type.update_by is '更新者'; comment on column sys_dict_type.update_time is '更新时间'; comment on column sys_dict_type.remark is '备注'; insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate, '', null, '用户性别列表'); insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate, '', null, '菜单状态列表'); insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate, '', null, '系统开关列表'); insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate, '', null, '系统是否列表'); insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate, '', null, '通知类型列表'); insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate, '', null, '通知状态列表'); insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate, '', null, '操作类型列表'); insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate, '', null, '登录状态列表'); -- ---------------------------- -- 12、字典数据表 -- ---------------------------- create table sys_dict_data ( dict_code number(20) not null, dict_sort number(4) default 0, dict_label varchar2(100) default '', dict_value varchar2(100) default '', dict_type varchar2(100) default '', css_class varchar2(100) default null, list_class varchar2(100) default null, is_default char(1) default 'N', status char(1) default '0', create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(500) default null ); alter table sys_dict_data add constraint pk_sys_dict_data primary key (dict_code); comment on table sys_dict_data is '字典数据表'; comment on column sys_dict_data.dict_code is '字典主键'; comment on column sys_dict_data.dict_sort is '字典排序'; comment on column sys_dict_data.dict_label is '字典标签'; comment on column sys_dict_data.dict_value is '字典键值'; comment on column sys_dict_data.dict_type is '字典类型'; comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; comment on column sys_dict_data.list_class is '表格回显样式'; comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; comment on column sys_dict_data.status is '状态(0正常 1停用)'; comment on column sys_dict_data.create_by is '创建者'; comment on column sys_dict_data.create_time is '创建时间'; comment on column sys_dict_data.update_by is '更新者'; comment on column sys_dict_data.update_time is '更新时间'; comment on column sys_dict_data.remark is '备注'; insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate, '', null, '性别男'); insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate, '', null, '性别女'); insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate, '', null, '性别未知'); insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '显示菜单'); insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '隐藏菜单'); insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '正常状态'); insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '停用状态'); insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '系统默认是'); insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '系统默认否'); insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate, '', null, '通知'); insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate, '', null, '公告'); insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate, '', null, '正常状态'); insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '关闭状态'); insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '其他操作'); insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '新增操作'); insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '修改操作'); insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '删除操作'); insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate, '', null, '授权操作'); insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate, '', null, '导出操作'); insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate, '', null, '导入操作'); insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '强退操作'); insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate, '', null, '生成操作'); insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '清空操作'); insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate, '', null, '正常状态'); insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate, '', null, '停用状态'); -- ---------------------------- -- 13、参数配置表 -- ---------------------------- create table sys_config ( config_id number(20) not null, config_name varchar2(100) default '', config_key varchar2(100) default '', config_value varchar2(100) default '', config_type char(1) default 'N', create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(500) default null ); alter table sys_config add constraint pk_sys_config primary key (config_id); comment on table sys_config is '参数配置表'; comment on column sys_config.config_id is '参数主键'; comment on column sys_config.config_name is '参数名称'; comment on column sys_config.config_key is '参数键名'; comment on column sys_config.config_value is '参数键值'; comment on column sys_config.config_type is '系统内置(Y是 N否)'; comment on column sys_config.create_by is '创建者'; comment on column sys_config.create_time is '创建时间'; comment on column sys_config.update_by is '更新者'; comment on column sys_config.update_time is '更新时间'; comment on column sys_config.remark is '备注'; insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate, '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate, '', null, '初始化密码 123456' ); insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate, '', null, '深色主题theme-dark,浅色主题theme-light' ); insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate, '', null, '是否开启验证码功能(true开启,false关闭)'); insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate, '', null, '是否开启注册用户功能(true开启,false关闭)'); insert into sys_config values(11, 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 'admin', sysdate, '', null, 'true:开启, false:关闭'); -- ---------------------------- -- 14、系统访问记录 -- ---------------------------- create table sys_logininfor ( info_id number(20) not null, user_name varchar2(50) default '', ipaddr varchar2(128) default '', login_location varchar2(255) default '', browser varchar2(50) default '', os varchar2(50) default '', status char(1) default '0', msg varchar2(255) default '', login_time date ); alter table sys_logininfor add constraint pk_sys_logininfor primary key (info_id); create index idx_sys_logininfor_s on sys_logininfor (status); create index idx_sys_logininfor_lt on sys_logininfor (login_time); comment on table sys_logininfor is '系统访问记录'; comment on column sys_logininfor.info_id is '访问ID'; comment on column sys_logininfor.user_name is '登录账号'; comment on column sys_logininfor.ipaddr is '登录IP地址'; comment on column sys_logininfor.login_location is '登录地点'; comment on column sys_logininfor.browser is '浏览器类型'; comment on column sys_logininfor.os is '操作系统'; comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; comment on column sys_logininfor.msg is '提示消息'; comment on column sys_logininfor.login_time is '访问时间'; -- ---------------------------- -- 17、通知公告表 -- ---------------------------- create table sys_notice ( notice_id number(20) not null, notice_title varchar2(50) not null, notice_type char(1) not null, notice_content clob default null, status char(1) default '0', create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(255) default null ); alter table sys_notice add constraint pk_sys_notice primary key (notice_id); comment on table sys_notice is '通知公告表'; comment on column sys_notice.notice_id is '公告主键'; comment on column sys_notice.notice_title is '公告标题'; comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; comment on column sys_notice.notice_content is '公告内容'; comment on column sys_notice.status is '公告状态(0正常 1关闭)'; comment on column sys_notice.create_by is '创建者'; comment on column sys_notice.create_time is '创建时间'; comment on column sys_notice.update_by is '更新者'; comment on column sys_notice.update_time is '更新时间'; comment on column sys_notice.remark is '备注'; -- ---------------------------- -- 初始化-公告信息表数据 -- ---------------------------- insert into sys_notice values('1', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate, '', null, '管理员'); insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate, '', null, '管理员'); -- ---------------------------- -- 18、代码生成业务表 -- ---------------------------- create table gen_table ( table_id number(20) not null, table_name varchar2(200) default '', table_comment varchar2(500) default '', sub_table_name varchar(64) default null, sub_table_fk_name varchar(64) default null, class_name varchar2(100) default '', tpl_category varchar2(200) default 'crud', package_name varchar2(100), module_name varchar2(30), business_name varchar2(30), function_name varchar2(50), function_author varchar2(50), gen_type char(1) default '0', gen_path varchar2(200) default '/', options varchar2(1000), create_by varchar2(64) default '', create_time date, update_by varchar2(64) default '', update_time date, remark varchar2(500) default null ); alter table gen_table add constraint pk_gen_table primary key (table_id); comment on table gen_table is '代码生成业务表'; comment on column gen_table.table_id is '编号'; comment on column gen_table.table_name is '表名称'; comment on column gen_table.table_comment is '表描述'; comment on column gen_table.sub_table_name is '关联子表的表名'; comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; comment on column gen_table.class_name is '实体类名称'; comment on column gen_table.tpl_category is '使用的模板(crud单表操作 tree树表操作)'; comment on column gen_table.package_name is '生成包路径'; comment on column gen_table.module_name is '生成模块名'; comment on column gen_table.business_name is '生成业务名'; comment on column gen_table.function_name is '生成功能名'; comment on column gen_table.function_author is '生成功能作者'; comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; comment on column gen_table.options is '其它生成选项'; comment on column gen_table.create_by is '创建者'; comment on column gen_table.create_time is '创建时间'; comment on column gen_table.update_by is '更新者'; comment on column gen_table.update_time is '更新时间'; comment on column gen_table.remark is '备注'; -- ---------------------------- -- 19、代码生成业务表字段 -- ---------------------------- create table gen_table_column ( column_id number(20) not null, table_id number(20), column_name varchar2(200), column_comment varchar2(500), column_type varchar2(100), java_type varchar2(500), java_field varchar2(200), is_pk char(1), is_increment char(1), is_required char(1), is_insert char(1), is_edit char(1), is_list char(1), is_query char(1), query_type varchar(200) default 'EQ', html_type varchar(200), dict_type varchar(200) default '', sort number(4), create_by varchar(64) default '', create_time date , update_by varchar(64) default '', update_time date ); alter table gen_table_column add constraint pk_gen_table_column primary key (column_id); comment on table gen_table_column is '代码生成业务表字段'; comment on column gen_table_column.column_id is '编号'; comment on column gen_table_column.table_id is '归属表编号'; comment on column gen_table_column.column_name is '列名称'; comment on column gen_table_column.column_comment is '列描述'; comment on column gen_table_column.column_type is '列类型'; comment on column gen_table_column.java_type is 'JAVA类型'; comment on column gen_table_column.java_field is 'JAVA字段名'; comment on column gen_table_column.is_pk is '是否主键(1是)'; comment on column gen_table_column.is_increment is '是否自增(1是)'; comment on column gen_table_column.is_required is '是否必填(1是)'; comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; comment on column gen_table_column.is_list is '是否列表字段(1是)'; comment on column gen_table_column.is_query is '是否查询字段(1是)'; comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; comment on column gen_table_column.dict_type is '字典类型'; comment on column gen_table_column.sort is '排序'; comment on column gen_table_column.create_by is '创建者'; comment on column gen_table_column.create_time is '创建时间'; comment on column gen_table_column.update_by is '更新者'; comment on column gen_table_column.update_time is '更新时间'; -- ---------------------------- -- OSS对象存储表 -- ---------------------------- create table sys_oss ( oss_id number(20) not null, file_name varchar(255) not null, original_name varchar(255) not null, file_suffix varchar(10) not null, url varchar(500) not null, service varchar(20) default 'minio' not null, create_by varchar(64) default '', create_time date, update_by varchar(64) default '', update_time date ); alter table sys_oss add constraint pk_sys_oss primary key (oss_id); comment on table sys_oss is 'OSS对象存储表'; comment on column sys_oss.oss_id is '对象存储主键'; comment on column sys_oss.file_name is '文件名'; comment on column sys_oss.original_name is '原名'; comment on column sys_oss.file_suffix is '文件后缀名'; comment on column sys_oss.url is 'URL地址'; comment on column sys_oss.service is '服务商'; comment on column sys_oss.create_time is '创建时间'; comment on column sys_oss.create_by is '上传者'; comment on column sys_oss.update_time is '更新时间'; comment on column sys_oss.update_by is '更新者'; -- ---------------------------- -- OSS对象存储动态配置表 -- ---------------------------- create table sys_oss_config ( oss_config_id number(20) not null, config_key varchar(20) not null, access_key varchar(255) default '', secret_key varchar(255) default '', bucket_name varchar(255) default '', prefix varchar(255) default '', endpoint varchar(255) default '', domain varchar(255) default '', is_https char(1) default 'N', region varchar(255) default '', access_policy char(1) default '1' not null, status char(1) default '1', ext1 varchar(255) default '', create_by varchar(64) default '', remark varchar(500) default null, create_time date, update_by varchar(64) default '', update_time date ); alter table sys_oss_config add constraint pk_sys_oss_config primary key (oss_config_id); comment on table sys_oss_config is '对象存储配置表'; comment on column sys_oss_config.oss_config_id is '主建'; comment on column sys_oss_config.config_key is '配置key'; comment on column sys_oss_config.access_key is 'accesskey'; comment on column sys_oss_config.secret_key is '秘钥'; comment on column sys_oss_config.bucket_name is '桶名称'; comment on column sys_oss_config.prefix is '前缀'; comment on column sys_oss_config.endpoint is '访问站点'; comment on column sys_oss_config.domain is '自定义域名'; comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; comment on column sys_oss_config.region is '域'; comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; comment on column sys_oss_config.status is '是否默认(0=是,1=否)'; comment on column sys_oss_config.ext1 is '扩展字段'; comment on column sys_oss_config.remark is '备注'; comment on column sys_oss_config.create_by is '创建者'; comment on column sys_oss_config.create_time is '创建时间'; comment on column sys_oss_config.update_by is '更新者'; comment on column sys_oss_config.update_time is '更新时间'; insert into sys_oss_config values (1, 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', '127.0.0.1:9000', '','N', '', '1', '0', '', NULL, 'admin', sysdate, 'admin', sysdate); insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); insert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', NULL, 'admin', sysdate, 'admin', sysdate); -- ---------------------------- -- 钩子 ,用于session连接之后 自动设置默认的date类型格式化 简化时间查询 -- 如需设置其它配置 可在此钩子内任意增加处理语句 -- 例如: SELECT * FROM sys_user WHERE create_time BETWEEN '2022-03-01 00:00:00' AND '2022-04-01 00:00:00' -- ---------------------------- create or replace trigger login_trg after logon on database begin execute immediate 'alter session set nls_date_format=''YYYY-MM-DD HH24:MI:SS'''; end; ================================================ FILE: script/sql/oracle/oracle_test.sql ================================================ create table test_demo ( id number(20) not null, dept_id number(20) default null, user_id number(20) default null, order_num number(10) default 0, test_key varchar2(255) default null, value varchar2(255) default null, version number(10) default 0, create_time date, create_by varchar2(64) default null, update_time date, update_by varchar2(64) default null, del_flag number(2) default 0 ); alter table test_demo add constraint pk_test_demo primary key (id); comment on table test_demo is '测试单表'; comment on column test_demo.id is '主键'; comment on column test_demo.dept_id is '部门id'; comment on column test_demo.user_id is '用户id'; comment on column test_demo.order_num is '排序号'; comment on column test_demo.test_key is 'key键'; comment on column test_demo.value is '值'; comment on column test_demo.version is '版本'; comment on column test_demo.create_time is '创建时间'; comment on column test_demo.create_by is '创建人'; comment on column test_demo.update_time is '更新时间'; comment on column test_demo.update_by is '更新人'; comment on column test_demo.del_flag is '删除标志'; create table test_tree ( id number(20) not null, parent_id number(20) default 0, dept_id number(20) default null, user_id number(20) default null, tree_name varchar2(255) default null, version number(10) default 0, create_time date, create_by varchar2(64) default null, update_time date, update_by varchar2(64) default null, del_flag number(2) default 0 ); alter table test_tree add constraint pk_test_tree primary key (id); comment on table test_tree is '测试树表'; comment on column test_tree.id is '主键'; comment on column test_tree.parent_id is '父id'; comment on column test_tree.dept_id is '部门id'; comment on column test_tree.user_id is '用户id'; comment on column test_tree.tree_name is '值'; comment on column test_tree.version is '版本'; comment on column test_tree.create_time is '创建时间'; comment on column test_tree.create_by is '创建人'; comment on column test_tree.update_time is '更新时间'; comment on column test_tree.update_by is '更新人'; comment on column test_tree.del_flag is '删除标志'; insert into sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) values (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, 'test', sysdate, null); insert into sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) values (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 'admin', sysdate, 'test1', sysdate, null); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (5, '测试菜单', 0, 5, 'demo', null, 1, 0, 'M', '0', '0', null, 'star', 'admin', sysdate, 'admin', sysdate, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', sysdate, '', null, '测试单表菜单'); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', sysdate, '', null, '测试树表菜单'); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', sysdate, '', null, ''); insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', sysdate, '', null, ''); insert into sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) values (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', sysdate, '', null, null); insert into sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) values (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 'admin', sysdate, '', null, null); insert into sys_role_menu(role_id, menu_id) values (3, 1); insert into sys_role_menu(role_id, menu_id) values (3, 5); insert into sys_role_menu(role_id, menu_id) values (3, 100); insert into sys_role_menu(role_id, menu_id) values (3, 101); insert into sys_role_menu(role_id, menu_id) values (3, 102); insert into sys_role_menu(role_id, menu_id) values (3, 103); insert into sys_role_menu(role_id, menu_id) values (3, 104); insert into sys_role_menu(role_id, menu_id) values (3, 105); insert into sys_role_menu(role_id, menu_id) values (3, 106); insert into sys_role_menu(role_id, menu_id) values (3, 107); insert into sys_role_menu(role_id, menu_id) values (3, 108); insert into sys_role_menu(role_id, menu_id) values (3, 500); insert into sys_role_menu(role_id, menu_id) values (3, 501); insert into sys_role_menu(role_id, menu_id) values (3, 1001); insert into sys_role_menu(role_id, menu_id) values (3, 1002); insert into sys_role_menu(role_id, menu_id) values (3, 1003); insert into sys_role_menu(role_id, menu_id) values (3, 1004); insert into sys_role_menu(role_id, menu_id) values (3, 1005); insert into sys_role_menu(role_id, menu_id) values (3, 1006); insert into sys_role_menu(role_id, menu_id) values (3, 1007); insert into sys_role_menu(role_id, menu_id) values (3, 1008); insert into sys_role_menu(role_id, menu_id) values (3, 1009); insert into sys_role_menu(role_id, menu_id) values (3, 1010); insert into sys_role_menu(role_id, menu_id) values (3, 1011); insert into sys_role_menu(role_id, menu_id) values (3, 1012); insert into sys_role_menu(role_id, menu_id) values (3, 1013); insert into sys_role_menu(role_id, menu_id) values (3, 1014); insert into sys_role_menu(role_id, menu_id) values (3, 1015); insert into sys_role_menu(role_id, menu_id) values (3, 1016); insert into sys_role_menu(role_id, menu_id) values (3, 1017); insert into sys_role_menu(role_id, menu_id) values (3, 1018); insert into sys_role_menu(role_id, menu_id) values (3, 1019); insert into sys_role_menu(role_id, menu_id) values (3, 1020); insert into sys_role_menu(role_id, menu_id) values (3, 1021); insert into sys_role_menu(role_id, menu_id) values (3, 1022); insert into sys_role_menu(role_id, menu_id) values (3, 1023); insert into sys_role_menu(role_id, menu_id) values (3, 1024); insert into sys_role_menu(role_id, menu_id) values (3, 1025); insert into sys_role_menu(role_id, menu_id) values (3, 1026); insert into sys_role_menu(role_id, menu_id) values (3, 1027); insert into sys_role_menu(role_id, menu_id) values (3, 1028); insert into sys_role_menu(role_id, menu_id) values (3, 1029); insert into sys_role_menu(role_id, menu_id) values (3, 1030); insert into sys_role_menu(role_id, menu_id) values (3, 1031); insert into sys_role_menu(role_id, menu_id) values (3, 1032); insert into sys_role_menu(role_id, menu_id) values (3, 1033); insert into sys_role_menu(role_id, menu_id) values (3, 1034); insert into sys_role_menu(role_id, menu_id) values (3, 1035); insert into sys_role_menu(role_id, menu_id) values (3, 1036); insert into sys_role_menu(role_id, menu_id) values (3, 1037); insert into sys_role_menu(role_id, menu_id) values (3, 1038); insert into sys_role_menu(role_id, menu_id) values (3, 1039); insert into sys_role_menu(role_id, menu_id) values (3, 1040); insert into sys_role_menu(role_id, menu_id) values (3, 1041); insert into sys_role_menu(role_id, menu_id) values (3, 1042); insert into sys_role_menu(role_id, menu_id) values (3, 1043); insert into sys_role_menu(role_id, menu_id) values (3, 1044); insert into sys_role_menu(role_id, menu_id) values (3, 1045); insert into sys_role_menu(role_id, menu_id) values (3, 1500); insert into sys_role_menu(role_id, menu_id) values (3, 1501); insert into sys_role_menu(role_id, menu_id) values (3, 1502); insert into sys_role_menu(role_id, menu_id) values (3, 1503); insert into sys_role_menu(role_id, menu_id) values (3, 1504); insert into sys_role_menu(role_id, menu_id) values (3, 1505); insert into sys_role_menu(role_id, menu_id) values (3, 1506); insert into sys_role_menu(role_id, menu_id) values (3, 1507); insert into sys_role_menu(role_id, menu_id) values (3, 1508); insert into sys_role_menu(role_id, menu_id) values (3, 1509); insert into sys_role_menu(role_id, menu_id) values (3, 1510); insert into sys_role_menu(role_id, menu_id) values (3, 1511); insert into sys_role_menu(role_id, menu_id) values (4, 5); insert into sys_role_menu(role_id, menu_id) values (4, 1500); insert into sys_role_menu(role_id, menu_id) values (4, 1501); insert into sys_role_menu(role_id, menu_id) values (4, 1502); insert into sys_role_menu(role_id, menu_id) values (4, 1503); insert into sys_role_menu(role_id, menu_id) values (4, 1504); insert into sys_role_menu(role_id, menu_id) values (4, 1505); insert into sys_role_menu(role_id, menu_id) values (4, 1506); insert into sys_role_menu(role_id, menu_id) values (4, 1507); insert into sys_role_menu(role_id, menu_id) values (4, 1508); insert into sys_role_menu(role_id, menu_id) values (4, 1509); insert into sys_role_menu(role_id, menu_id) values (4, 1510); insert into sys_role_menu(role_id, menu_id) values (4, 1511); insert into sys_user_role(user_id, role_id) values (3, 3); insert into sys_user_role(user_id, role_id) values (4, 4); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (1, 102, 4, 1, '测试数据权限', '测试', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (2, 102, 3, 2, '子节点1', '111', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (3, 102, 3, 3, '子节点2', '222', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (4, 108, 4, 4, '测试数据', 'demo', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (5, 108, 3, 13, '子节点11', '1111', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (6, 108, 3, 12, '子节点22', '2222', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (7, 108, 3, 11, '子节点33', '3333', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (8, 108, 3, 10, '子节点44', '4444', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (9, 108, 3, 9, '子节点55', '5555', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (10, 108, 3, 8, '子节点66', '6666', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (11, 108, 3, 7, '子节点77', '7777', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (12, 108, 3, 6, '子节点88', '8888', 0, sysdate, 'admin', null, '', 0); insert into test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) values (13, 108, 3, 5, '子节点99', '9999', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (1, 0, 102, 4, '测试数据权限', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (2, 1, 102, 3, '子节点1', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (3, 2, 102, 3, '子节点2', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (4, 0, 108, 4, '测试树1', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (5, 4, 108, 3, '子节点11', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (6, 4, 108, 3, '子节点22', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (7, 4, 108, 3, '子节点33', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (8, 5, 108, 3, '子节点44', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (9, 6, 108, 3, '子节点55', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (10, 7, 108, 3, '子节点66', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (11, 7, 108, 3, '子节点77', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (12, 10, 108, 3, '子节点88', 0, sysdate, 'admin', null, '', 0); insert into test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) values (13, 10, 108, 3, '子节点99', 0, sysdate, 'admin', null, '', 0); ================================================ FILE: script/sql/postgres/postgres_ry_vue_4.X.sql ================================================ -- ---------------------------- -- 1、部门表 -- ---------------------------- drop table if exists sys_dept; create table if not exists sys_dept ( dept_id int8, parent_id int8 default 0, ancestors varchar(500)default ''::varchar, dept_name varchar(30) default ''::varchar, order_num int4 default 0, leader varchar(20) default null::varchar, phone varchar(11) default null::varchar, email varchar(50) default null::varchar, status char default '0'::bpchar, del_flag char default '0'::bpchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, constraint "sys_dept_pk" primary key (dept_id) ); comment on table sys_dept is '部门表'; comment on column sys_dept.dept_id is '部门ID'; comment on column sys_dept.parent_id is '父部门ID'; comment on column sys_dept.ancestors is '祖级列表'; comment on column sys_dept.dept_name is '部门名称'; comment on column sys_dept.order_num is '显示顺序'; comment on column sys_dept.leader is '负责人'; comment on column sys_dept.phone is '联系电话'; comment on column sys_dept.email is '邮箱'; comment on column sys_dept.status is '部门状态(0正常 1停用)'; comment on column sys_dept.del_flag is '删除标志(0代表存在 2代表删除)'; comment on column sys_dept.create_by is '创建者'; comment on column sys_dept.create_time is '创建时间'; comment on column sys_dept.update_by is '更新者'; comment on column sys_dept.update_time is '更新时间'; -- ---------------------------- -- 初始化-部门表数据 -- ---------------------------- insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', now(), '', null); -- ---------------------------- -- 2、用户信息表 -- ---------------------------- drop table if exists sys_user; create table if not exists sys_user ( user_id int8, dept_id int8, user_name varchar(30) not null, nick_name varchar(30) not null, user_type varchar(10) default 'sys_user'::varchar, email varchar(50) default ''::varchar, phonenumber varchar(11) default ''::varchar, sex char default '0'::bpchar, avatar varchar(100) default ''::varchar, password varchar(100) default ''::varchar, status char default '0'::bpchar, del_flag char default '0'::bpchar, login_ip varchar(128) default ''::varchar, login_date timestamp, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default null::varchar, constraint "sys_user_pk" primary key (user_id) ); comment on table sys_user is '用户信息表'; comment on column sys_user.user_id is '用户ID'; comment on column sys_user.dept_id is '部门ID'; comment on column sys_user.user_name is '用户账号'; comment on column sys_user.nick_name is '用户昵称'; comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; comment on column sys_user.email is '用户邮箱'; comment on column sys_user.phonenumber is '手机号码'; comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; comment on column sys_user.avatar is '头像地址'; comment on column sys_user.password is '密码'; comment on column sys_user.status is '帐号状态(0正常 1停用)'; comment on column sys_user.del_flag is '删除标志(0代表存在 2代表删除)'; comment on column sys_user.login_ip is '最后登陆IP'; comment on column sys_user.login_date is '最后登陆时间'; comment on column sys_user.create_by is '创建者'; comment on column sys_user.create_time is '创建时间'; comment on column sys_user.update_by is '更新者'; comment on column sys_user.update_time is '更新时间'; comment on column sys_user.remark is '备注'; -- ---------------------------- -- 初始化-用户信息表数据 -- ---------------------------- insert into sys_user values(1, 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 'admin', now(), '', null, '管理员'); insert into sys_user values(2, 105, 'ry', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 'admin', now(), '', null, '测试员'); -- ---------------------------- -- 3、岗位信息表 -- ---------------------------- drop table if exists sys_post; create table if not exists sys_post ( post_id int8, post_code varchar(64) not null, post_name varchar(50) not null, post_sort int4 not null, status char not null, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default null::varchar, constraint "sys_post_pk" primary key (post_id) ); comment on table sys_post is '岗位信息表'; comment on column sys_post.post_id is '岗位ID'; comment on column sys_post.post_code is '岗位编码'; comment on column sys_post.post_name is '岗位名称'; comment on column sys_post.post_sort is '显示顺序'; comment on column sys_post.status is '状态(0正常 1停用)'; comment on column sys_post.create_by is '创建者'; comment on column sys_post.create_time is '创建时间'; comment on column sys_post.update_by is '更新者'; comment on column sys_post.update_time is '更新时间'; comment on column sys_post.remark is '备注'; -- ---------------------------- -- 初始化-岗位信息表数据 -- ---------------------------- insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', now(), '', null, ''); insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', now(), '', null, ''); insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', now(), '', null, ''); insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', now(), '', null, ''); -- ---------------------------- -- 4、角色信息表 -- ---------------------------- drop table if exists sys_role; create table if not exists sys_role ( role_id int8, role_name varchar(30) not null, role_key varchar(100) not null, role_sort int4 not null, data_scope char default '1'::bpchar, menu_check_strictly bool default true, dept_check_strictly bool default true, status char not null, del_flag char default '0'::bpchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default null::varchar, constraint "sys_role_pk" primary key (role_id) ); comment on table sys_role is '角色信息表'; comment on column sys_role.role_id is '角色ID'; comment on column sys_role.role_name is '角色名称'; comment on column sys_role.role_key is '角色权限字符串'; comment on column sys_role.role_sort is '显示顺序'; comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; comment on column sys_role.status is '角色状态(0正常 1停用)'; comment on column sys_role.del_flag is '删除标志(0代表存在 2代表删除)'; comment on column sys_role.create_by is '创建者'; comment on column sys_role.create_time is '创建时间'; comment on column sys_role.update_by is '更新者'; comment on column sys_role.update_time is '更新时间'; comment on column sys_role.remark is '备注'; -- ---------------------------- -- 初始化-角色信息表数据 -- ---------------------------- insert into sys_role values('1', '超级管理员', 'admin', 1, '1', 't', 't', '0', '0', 'admin', now(), '', null, '超级管理员'); insert into sys_role values('2', '普通角色', 'common', 2, '2', 't', 't', '0', '0', 'admin', now(), '', null, '普通角色'); -- ---------------------------- -- 5、菜单权限表 -- ---------------------------- drop table if exists sys_menu; create table if not exists sys_menu ( menu_id int8, menu_name varchar(50) not null, parent_id int8 default 0, order_num int4 default 0, path varchar(200) default ''::varchar, component varchar(255) default null::varchar, query_param varchar(255) default null::varchar, is_frame char default '1'::bpchar, is_cache char default '0'::bpchar, menu_type char default ''::bpchar, visible char default '0'::bpchar, status char default '0'::bpchar, perms varchar(100) default null::varchar, icon varchar(100) default '#'::varchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default ''::varchar, constraint "sys_menu_pk" primary key (menu_id) ); comment on table sys_menu is '菜单权限表'; comment on column sys_menu.menu_id is '菜单ID'; comment on column sys_menu.menu_name is '菜单名称'; comment on column sys_menu.parent_id is '父菜单ID'; comment on column sys_menu.order_num is '显示顺序'; comment on column sys_menu.path is '路由地址'; comment on column sys_menu.component is '组件路径'; comment on column sys_menu.query_param is '路由参数'; comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; comment on column sys_menu.status is '菜单状态(0正常 1停用)'; comment on column sys_menu.perms is '权限标识'; comment on column sys_menu.icon is '菜单图标'; comment on column sys_menu.create_by is '创建者'; comment on column sys_menu.create_time is '创建时间'; comment on column sys_menu.update_by is '更新者'; comment on column sys_menu.update_time is '更新时间'; comment on column sys_menu.remark is '备注'; -- ---------------------------- -- 初始化-菜单信息表数据 -- ---------------------------- -- 一级菜单 insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', '1', '0', 'M', '0', '0', '', 'system', 'admin', now(), '', null, '系统管理目录'); insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', '1', '0', 'M', '0', '0', '', 'monitor', 'admin', now(), '', null, '系统监控目录'); insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', '1', '0', 'M', '0', '0', '', 'tool', 'admin', now(), '', null, '系统工具目录'); insert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', '0', '0', 'M', '0', '0', '', 'guide', 'admin', now(), '', null, 'RuoYi-Vue-Plus官网地址'); -- 二级菜单 insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', '1', '0', 'C', '0', '0', 'system:user:list', 'user', 'admin', now(), '', null, '用户管理菜单'); insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', '1', '0', 'C', '0', '0', 'system:role:list', 'peoples', 'admin', now(), '', null, '角色管理菜单'); insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', '1', '0', 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', now(), '', null, '菜单管理菜单'); insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', '1', '0', 'C', '0', '0', 'system:dept:list', 'tree', 'admin', now(), '', null, '部门管理菜单'); insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', '1', '0', 'C', '0', '0', 'system:post:list', 'post', 'admin', now(), '', null, '岗位管理菜单'); insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', '1', '0', 'C', '0', '0', 'system:dict:list', 'dict', 'admin', now(), '', null, '字典管理菜单'); insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', '1', '0', 'C', '0', '0', 'system:config:list', 'edit', 'admin', now(), '', null, '参数设置菜单'); insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', '1', '0', 'C', '0', '0', 'system:notice:list', 'message', 'admin', now(), '', null, '通知公告菜单'); insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', '1', '0', 'M', '0', '0', '', 'log', 'admin', now(), '', null, '日志管理菜单'); insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', '1', '0', 'C', '0', '0', 'monitor:online:list', 'online', 'admin', now(), '', null, '在线用户菜单'); insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', now(), '', null, '缓存列表菜单'); insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', now(), '', null, '缓存监控菜单'); insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', '1', '0', 'C', '0', '0', 'tool:build:list', 'build', 'admin', now(), '', null, '表单构建菜单'); insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', '1', '0', 'C', '0', '0', 'tool:gen:list', 'code', 'admin', now(), '', null, '代码生成菜单'); -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', '1', '0', 'C', '0', '0', 'monitor:admin:list', 'dashboard', 'admin', now(), '', null, 'Admin监控菜单'); -- oss菜单 insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', '1', '0', 'C', '0', '0', 'system:oss:list', 'upload', 'admin', now(), '', null, '文件管理菜单'); -- xxl-job-admin控制台 insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', '1', '0', 'C', '0', '0', 'monitor:xxljob:list', 'job', 'admin', now(), '', null, 'Xxl-Job控制台菜单'); -- 三级菜单 insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', '1', '0', 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', now(), '', null, '操作日志菜单'); insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', '1', '0', 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', now(), '', null, '登录日志菜单'); -- 用户管理按钮 insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:user:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:user:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:user:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:user:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:user:export', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', '1', '0', 'F', '0', '0', 'system:user:import', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', '1', '0', 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', now(), '', null, ''); -- 角色管理按钮 insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:role:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:role:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:role:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:role:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:role:export', '#', 'admin', now(), '', null, ''); -- 菜单管理按钮 insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:remove', '#', 'admin', now(), '', null, ''); -- 部门管理按钮 insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:remove', '#', 'admin', now(), '', null, ''); -- 岗位管理按钮 insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:post:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:post:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:post:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:post:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:post:export', '#', 'admin', now(), '', null, ''); -- 字典管理按钮 insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:export', '#', 'admin', now(), '', null, ''); -- 参数设置按钮 insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:export', '#', 'admin', now(), '', null, ''); -- 通知公告按钮 insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:remove', '#', 'admin', now(), '', null, ''); -- 操作日志按钮 insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', now(), '', null, ''); -- 登录日志按钮 insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', now(), '', null, ''); -- 在线用户按钮 insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', now(), '', null, ''); -- 代码生成按钮 insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:edit', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:import', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:preview', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:code', '#', 'admin', now(), '', null, ''); -- oss相关按钮 insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:query', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:upload', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:download', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:remove', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:add', '#', 'admin', now(), '', null, ''); insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:edit', '#', 'admin', now(), '', null, ''); -- ---------------------------- -- 6、用户和角色关联表 用户N-1角色 -- ---------------------------- drop table if exists sys_user_role; create table if not exists sys_user_role ( user_id int8 not null, role_id int8 not null, constraint sys_user_role_pk primary key (user_id, role_id) ); comment on table sys_user_role is '用户和角色关联表'; comment on column sys_user_role.user_id is '用户ID'; comment on column sys_user_role.role_id is '角色ID'; -- ---------------------------- -- 初始化-用户和角色关联表数据 -- ---------------------------- insert into sys_user_role values ('1', '1'); insert into sys_user_role values ('2', '2'); -- ---------------------------- -- 7、角色和菜单关联表 角色1-N菜单 -- ---------------------------- drop table if exists sys_role_menu; create table if not exists sys_role_menu ( role_id int8 not null, menu_id int8 not null, constraint sys_role_menu_pk primary key (role_id, menu_id) ); comment on table sys_role_menu is '角色和菜单关联表'; comment on column sys_role_menu.role_id is '角色ID'; comment on column sys_role_menu.menu_id is '菜单ID'; -- ---------------------------- -- 初始化-角色和菜单关联表数据 -- ---------------------------- insert into sys_role_menu values ('2', '1'); insert into sys_role_menu values ('2', '2'); insert into sys_role_menu values ('2', '3'); insert into sys_role_menu values ('2', '4'); insert into sys_role_menu values ('2', '100'); insert into sys_role_menu values ('2', '101'); insert into sys_role_menu values ('2', '102'); insert into sys_role_menu values ('2', '103'); insert into sys_role_menu values ('2', '104'); insert into sys_role_menu values ('2', '105'); insert into sys_role_menu values ('2', '106'); insert into sys_role_menu values ('2', '107'); insert into sys_role_menu values ('2', '108'); insert into sys_role_menu values ('2', '109'); insert into sys_role_menu values ('2', '110'); insert into sys_role_menu values ('2', '111'); insert into sys_role_menu values ('2', '112'); insert into sys_role_menu values ('2', '113'); insert into sys_role_menu values ('2', '114'); insert into sys_role_menu values ('2', '115'); insert into sys_role_menu values ('2', '116'); insert into sys_role_menu values ('2', '500'); insert into sys_role_menu values ('2', '501'); insert into sys_role_menu values ('2', '1000'); insert into sys_role_menu values ('2', '1001'); insert into sys_role_menu values ('2', '1002'); insert into sys_role_menu values ('2', '1003'); insert into sys_role_menu values ('2', '1004'); insert into sys_role_menu values ('2', '1005'); insert into sys_role_menu values ('2', '1006'); insert into sys_role_menu values ('2', '1007'); insert into sys_role_menu values ('2', '1008'); insert into sys_role_menu values ('2', '1009'); insert into sys_role_menu values ('2', '1010'); insert into sys_role_menu values ('2', '1011'); insert into sys_role_menu values ('2', '1012'); insert into sys_role_menu values ('2', '1013'); insert into sys_role_menu values ('2', '1014'); insert into sys_role_menu values ('2', '1015'); insert into sys_role_menu values ('2', '1016'); insert into sys_role_menu values ('2', '1017'); insert into sys_role_menu values ('2', '1018'); insert into sys_role_menu values ('2', '1019'); insert into sys_role_menu values ('2', '1020'); insert into sys_role_menu values ('2', '1021'); insert into sys_role_menu values ('2', '1022'); insert into sys_role_menu values ('2', '1023'); insert into sys_role_menu values ('2', '1024'); insert into sys_role_menu values ('2', '1025'); insert into sys_role_menu values ('2', '1026'); insert into sys_role_menu values ('2', '1027'); insert into sys_role_menu values ('2', '1028'); insert into sys_role_menu values ('2', '1029'); insert into sys_role_menu values ('2', '1030'); insert into sys_role_menu values ('2', '1031'); insert into sys_role_menu values ('2', '1032'); insert into sys_role_menu values ('2', '1033'); insert into sys_role_menu values ('2', '1034'); insert into sys_role_menu values ('2', '1035'); insert into sys_role_menu values ('2', '1036'); insert into sys_role_menu values ('2', '1037'); insert into sys_role_menu values ('2', '1038'); insert into sys_role_menu values ('2', '1039'); insert into sys_role_menu values ('2', '1040'); insert into sys_role_menu values ('2', '1041'); insert into sys_role_menu values ('2', '1042'); insert into sys_role_menu values ('2', '1043'); insert into sys_role_menu values ('2', '1044'); insert into sys_role_menu values ('2', '1045'); insert into sys_role_menu values ('2', '1050'); insert into sys_role_menu values ('2', '1046'); insert into sys_role_menu values ('2', '1047'); insert into sys_role_menu values ('2', '1048'); insert into sys_role_menu values ('2', '1055'); insert into sys_role_menu values ('2', '1056'); insert into sys_role_menu values ('2', '1057'); insert into sys_role_menu values ('2', '1058'); insert into sys_role_menu values ('2', '1059'); insert into sys_role_menu values ('2', '1060'); -- ---------------------------- -- 8、角色和部门关联表 角色1-N部门 -- ---------------------------- drop table if exists sys_role_dept; create table if not exists sys_role_dept ( role_id int8 not null, dept_id int8 not null, constraint sys_role_dept_pk primary key (role_id, dept_id) ); comment on table sys_role_dept is '角色和部门关联表'; comment on column sys_role_dept.role_id is '角色ID'; comment on column sys_role_dept.dept_id is '部门ID'; -- ---------------------------- -- 初始化-角色和部门关联表数据 -- ---------------------------- insert into sys_role_dept values ('2', '100'); insert into sys_role_dept values ('2', '101'); insert into sys_role_dept values ('2', '105'); -- ---------------------------- -- 9、用户与岗位关联表 用户1-N岗位 -- ---------------------------- drop table if exists sys_user_post; create table if not exists sys_user_post ( user_id int8 not null, post_id int8 not null, constraint sys_user_post_pk primary key (user_id, post_id) ); comment on table sys_user_post is '用户与岗位关联表'; comment on column sys_user_post.user_id is '用户ID'; comment on column sys_user_post.post_id is '岗位ID'; -- ---------------------------- -- 初始化-用户与岗位关联表数据 -- ---------------------------- insert into sys_user_post values ('1', '1'); insert into sys_user_post values ('2', '2'); -- ---------------------------- -- 10、操作日志记录 -- ---------------------------- drop table if exists sys_oper_log; create table if not exists sys_oper_log ( oper_id int8, title varchar(50) default ''::varchar, business_type int4 default 0, method varchar(100) default ''::varchar, request_method varchar(10) default ''::varchar, operator_type int4 default 0, oper_name varchar(50) default ''::varchar, dept_name varchar(50) default ''::varchar, oper_url varchar(255) default ''::varchar, oper_ip varchar(128) default ''::varchar, oper_location varchar(255) default ''::varchar, oper_param varchar(2000) default ''::varchar, json_result varchar(2000) default ''::varchar, status int4 default 0, error_msg varchar(2000) default ''::varchar, oper_time timestamp, constraint sys_oper_log_pk primary key (oper_id) ); create index idx_sys_oper_log_bt ON sys_oper_log (business_type); create index idx_sys_oper_log_s ON sys_oper_log (status); create index idx_sys_oper_log_ot ON sys_oper_log (oper_time); comment on table sys_oper_log is '操作日志记录'; comment on column sys_oper_log.oper_id is '日志主键'; comment on column sys_oper_log.title is '模块标题'; comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; comment on column sys_oper_log.method is '方法名称'; comment on column sys_oper_log.request_method is '请求方式'; comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; comment on column sys_oper_log.oper_name is '操作人员'; comment on column sys_oper_log.dept_name is '部门名称'; comment on column sys_oper_log.oper_url is '请求URL'; comment on column sys_oper_log.oper_ip is '主机地址'; comment on column sys_oper_log.oper_location is '操作地点'; comment on column sys_oper_log.oper_param is '请求参数'; comment on column sys_oper_log.json_result is '返回参数'; comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; comment on column sys_oper_log.error_msg is '错误消息'; comment on column sys_oper_log.oper_time is '操作时间'; -- ---------------------------- -- 11、字典类型表 -- ---------------------------- drop table if exists sys_dict_type; create table if not exists sys_dict_type ( dict_id int8, dict_name varchar(100) default ''::varchar, dict_type varchar(100) default ''::varchar, status char default '0'::bpchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default null::varchar, constraint sys_dict_type_pk primary key (dict_id) ); create unique index sys_dict_type_index1 ON sys_dict_type (dict_type); comment on table sys_dict_type is '字典类型表'; comment on column sys_dict_type.dict_id is '字典主键'; comment on column sys_dict_type.dict_name is '字典名称'; comment on column sys_dict_type.dict_type is '字典类型'; comment on column sys_dict_type.status is '状态(0正常 1停用)'; comment on column sys_dict_type.create_by is '创建者'; comment on column sys_dict_type.create_time is '创建时间'; comment on column sys_dict_type.update_by is '更新者'; comment on column sys_dict_type.update_time is '更新时间'; comment on column sys_dict_type.remark is '备注'; insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', now(), '', null, '用户性别列表'); insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', now(), '', null, '菜单状态列表'); insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', now(), '', null, '系统开关列表'); insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', now(), '', null, '系统是否列表'); insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', now(), '', null, '通知类型列表'); insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', now(), '', null, '通知状态列表'); insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', now(), '', null, '操作类型列表'); insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', now(), '', null, '登录状态列表'); -- ---------------------------- -- 12、字典数据表 -- ---------------------------- drop table if exists sys_dict_data; create table if not exists sys_dict_data ( dict_code int8, dict_sort int4 default 0, dict_label varchar(100) default ''::varchar, dict_value varchar(100) default ''::varchar, dict_type varchar(100) default ''::varchar, css_class varchar(100) default null::varchar, list_class varchar(100) default null::varchar, is_default char default 'N'::bpchar, status char default '0'::bpchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default null::varchar, constraint sys_dict_data_pk primary key (dict_code) ); comment on table sys_dict_data is '字典数据表'; comment on column sys_dict_data.dict_code is '字典编码'; comment on column sys_dict_data.dict_sort is '字典排序'; comment on column sys_dict_data.dict_label is '字典标签'; comment on column sys_dict_data.dict_value is '字典键值'; comment on column sys_dict_data.dict_type is '字典类型'; comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; comment on column sys_dict_data.list_class is '表格回显样式'; comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; comment on column sys_dict_data.status is '状态(0正常 1停用)'; comment on column sys_dict_data.create_by is '创建者'; comment on column sys_dict_data.create_time is '创建时间'; comment on column sys_dict_data.update_by is '更新者'; comment on column sys_dict_data.update_time is '更新时间'; comment on column sys_dict_data.remark is '备注'; insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', now(), '', null, '性别男'); insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', now(), '', null, '性别女'); insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', now(), '', null, '性别未知'); insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', now(), '', null, '显示菜单'); insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', now(), '', null, '隐藏菜单'); insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', now(), '', null, '正常状态'); insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', now(), '', null, '停用状态'); insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', now(), '', null, '系统默认是'); insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', now(), '', null, '系统默认否'); insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', now(), '', null, '通知'); insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', now(), '', null, '公告'); insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', now(), '', null, '正常状态'); insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', now(), '', null, '关闭状态'); insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '其他操作'); insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '新增操作'); insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '修改操作'); insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', now(), '', null, '删除操作'); insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', now(), '', null, '授权操作'); insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', now(), '', null, '导出操作'); insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', now(), '', null, '导入操作'); insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', now(), '', null, '强退操作'); insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', now(), '', null, '生成操作'); insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', now(), '', null, '清空操作'); insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', now(), '', null, '正常状态'); insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', now(), '', null, '停用状态'); -- ---------------------------- -- 13、参数配置表 -- ---------------------------- drop table if exists sys_config; create table if not exists sys_config ( config_id int8, config_name varchar(100) default ''::varchar, config_key varchar(100) default ''::varchar, config_value varchar(500) default ''::varchar, config_type char default 'N'::bpchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default null::varchar, constraint sys_config_pk primary key (config_id) ); comment on table sys_config is '参数配置表'; comment on column sys_config.config_id is '参数主键'; comment on column sys_config.config_name is '参数名称'; comment on column sys_config.config_key is '参数键名'; comment on column sys_config.config_value is '参数键值'; comment on column sys_config.config_type is '系统内置(Y是 N否)'; comment on column sys_config.create_by is '创建者'; comment on column sys_config.create_time is '创建时间'; comment on column sys_config.update_by is '更新者'; comment on column sys_config.update_time is '更新时间'; comment on column sys_config.remark is '备注'; insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', now(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', now(), '', null, '初始化密码 123456' ); insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', now(), '', null, '深色主题theme-dark,浅色主题theme-light' ); insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', now(), '', null, '是否开启验证码功能(true开启,false关闭)'); insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', now(), '', null, '是否开启注册用户功能(true开启,false关闭)'); insert into sys_config values(11, 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 'admin', now(), '', null, 'true:开启, false:关闭'); -- ---------------------------- -- 14、系统访问记录 -- ---------------------------- drop table if exists sys_logininfor; create table if not exists sys_logininfor ( info_id int8, user_name varchar(50) default ''::varchar, ipaddr varchar(128) default ''::varchar, login_location varchar(255) default ''::varchar, browser varchar(50) default ''::varchar, os varchar(50) default ''::varchar, status char default '0'::bpchar, msg varchar(255) default ''::varchar, login_time timestamp, constraint sys_logininfor_pk primary key (info_id) ); create index idx_sys_logininfor_s ON sys_logininfor (status); create index idx_sys_logininfor_lt ON sys_logininfor (login_time); comment on table sys_logininfor is '系统访问记录'; comment on column sys_logininfor.info_id is '访问ID'; comment on column sys_logininfor.user_name is '用户账号'; comment on column sys_logininfor.ipaddr is '登录IP地址'; comment on column sys_logininfor.login_location is '登录地点'; comment on column sys_logininfor.browser is '浏览器类型'; comment on column sys_logininfor.os is '操作系统'; comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; comment on column sys_logininfor.msg is '提示消息'; comment on column sys_logininfor.login_time is '访问时间'; -- ---------------------------- -- 17、通知公告表 -- ---------------------------- drop table if exists sys_notice; create table if not exists sys_notice ( notice_id int8, notice_title varchar(50) not null, notice_type char not null, notice_content text, status char default '0'::bpchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(255) default null::varchar, constraint sys_notice_pk primary key (notice_id) ); comment on table sys_notice is '通知公告表'; comment on column sys_notice.notice_id is '公告ID'; comment on column sys_notice.notice_title is '公告标题'; comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; comment on column sys_notice.notice_content is '公告内容'; comment on column sys_notice.status is '公告状态(0正常 1关闭)'; comment on column sys_notice.create_by is '创建者'; comment on column sys_notice.create_time is '创建时间'; comment on column sys_notice.update_by is '更新者'; comment on column sys_notice.update_time is '更新时间'; comment on column sys_notice.remark is '备注'; -- ---------------------------- -- 初始化-公告信息表数据 -- ---------------------------- insert into sys_notice values('1', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', now(), '', null, '管理员'); insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 'admin', now(), '', null, '管理员'); -- ---------------------------- -- 18、代码生成业务表 -- ---------------------------- drop table if exists gen_table; create table if not exists gen_table ( table_id int8, table_name varchar(200) default ''::varchar, table_comment varchar(500) default ''::varchar, sub_table_name varchar(64) default ''::varchar, sub_table_fk_name varchar(64) default ''::varchar, class_name varchar(100) default ''::varchar, tpl_category varchar(200) default 'crud'::varchar, package_name varchar(100) default null::varchar, module_name varchar(30) default null::varchar, business_name varchar(30) default null::varchar, function_name varchar(50) default null::varchar, function_author varchar(50) default null::varchar, gen_type char default '0'::bpchar not null, gen_path varchar(200) default '/'::varchar, options varchar(1000) default null::varchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default null::varchar, constraint gen_table_pk primary key (table_id) ); comment on table gen_table is '代码生成业务表'; comment on column gen_table.table_id is '编号'; comment on column gen_table.table_name is '表名称'; comment on column gen_table.table_comment is '表描述'; comment on column gen_table.sub_table_name is '关联子表的表名'; comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; comment on column gen_table.class_name is '实体类名称'; comment on column gen_table.tpl_category is '使用的模板(CRUD单表操作 TREE树表操作)'; comment on column gen_table.package_name is '生成包路径'; comment on column gen_table.module_name is '生成模块名'; comment on column gen_table.business_name is '生成业务名'; comment on column gen_table.function_name is '生成功能名'; comment on column gen_table.function_author is '生成功能作者'; comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; comment on column gen_table.options is '其它生成选项'; comment on column gen_table.create_by is '创建者'; comment on column gen_table.create_time is '创建时间'; comment on column gen_table.update_by is '更新者'; comment on column gen_table.update_time is '更新时间'; comment on column gen_table.remark is '备注'; -- ---------------------------- -- 19、代码生成业务表字段 -- ---------------------------- drop table if exists gen_table_column; create table if not exists gen_table_column ( column_id int8, table_id int8, column_name varchar(200) default null::varchar, column_comment varchar(500) default null::varchar, column_type varchar(100) default null::varchar, java_type varchar(500) default null::varchar, java_field varchar(200) default null::varchar, is_pk char default null::bpchar, is_increment char default null::bpchar, is_required char default null::bpchar, is_insert char default null::bpchar, is_edit char default null::bpchar, is_list char default null::bpchar, is_query char default null::bpchar, query_type varchar(200) default 'EQ'::varchar, html_type varchar(200) default null::varchar, dict_type varchar(200) default ''::varchar, sort int4, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, constraint gen_table_column_pk primary key (column_id) ); comment on table gen_table_column is '代码生成业务表字段'; comment on column gen_table_column.column_id is '编号'; comment on column gen_table_column.table_id is '归属表编号'; comment on column gen_table_column.column_name is '列名称'; comment on column gen_table_column.column_comment is '列描述'; comment on column gen_table_column.column_type is '列类型'; comment on column gen_table_column.java_type is 'JAVA类型'; comment on column gen_table_column.java_field is 'JAVA字段名'; comment on column gen_table_column.is_pk is '是否主键(1是)'; comment on column gen_table_column.is_increment is '是否自增(1是)'; comment on column gen_table_column.is_required is '是否必填(1是)'; comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; comment on column gen_table_column.is_list is '是否列表字段(1是)'; comment on column gen_table_column.is_query is '是否查询字段(1是)'; comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; comment on column gen_table_column.dict_type is '字典类型'; comment on column gen_table_column.sort is '排序'; comment on column gen_table_column.create_by is '创建者'; comment on column gen_table_column.create_time is '创建时间'; comment on column gen_table_column.update_by is '更新者'; comment on column gen_table_column.update_time is '更新时间'; -- ---------------------------- -- OSS对象存储表 -- ---------------------------- drop table if exists sys_oss; create table if not exists sys_oss ( oss_id int8, file_name varchar(255) default ''::varchar not null, original_name varchar(255) default ''::varchar not null, file_suffix varchar(10) default ''::varchar not null, url varchar(500) default ''::varchar not null, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, service varchar(20) default 'minio'::varchar, constraint sys_oss_pk primary key (oss_id) ); comment on table sys_oss is 'OSS对象存储表'; comment on column sys_oss.oss_id is '对象存储主键'; comment on column sys_oss.file_name is '文件名'; comment on column sys_oss.original_name is '原名'; comment on column sys_oss.file_suffix is '文件后缀名'; comment on column sys_oss.url is 'URL地址'; comment on column sys_oss.create_by is '上传人'; comment on column sys_oss.create_time is '创建时间'; comment on column sys_oss.update_by is '更新者'; comment on column sys_oss.update_time is '更新时间'; comment on column sys_oss.service is '服务商'; -- ---------------------------- -- OSS对象存储动态配置表 -- ---------------------------- drop table if exists sys_oss_config; create table if not exists sys_oss_config ( oss_config_id int8, config_key varchar(20) default ''::varchar not null, access_key varchar(255) default ''::varchar, secret_key varchar(255) default ''::varchar, bucket_name varchar(255) default ''::varchar, prefix varchar(255) default ''::varchar, endpoint varchar(255) default ''::varchar, domain varchar(255) default ''::varchar, is_https char default 'N'::bpchar, region varchar(255) default ''::varchar, access_policy char(1) default '1'::bpchar not null, status char default '1'::bpchar, ext1 varchar(255) default ''::varchar, create_by varchar(64) default ''::varchar, create_time timestamp, update_by varchar(64) default ''::varchar, update_time timestamp, remark varchar(500) default ''::varchar, constraint sys_oss_config_pk primary key (oss_config_id) ); comment on table sys_oss_config is '对象存储配置表'; comment on column sys_oss_config.oss_config_id is '主建'; comment on column sys_oss_config.config_key is '配置key'; comment on column sys_oss_config.access_key is 'accessKey'; comment on column sys_oss_config.secret_key is '秘钥'; comment on column sys_oss_config.bucket_name is '桶名称'; comment on column sys_oss_config.prefix is '前缀'; comment on column sys_oss_config.endpoint is '访问站点'; comment on column sys_oss_config.domain is '自定义域名'; comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; comment on column sys_oss_config.region is '域'; comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; comment on column sys_oss_config.status is '是否默认(0=是,1=否)'; comment on column sys_oss_config.ext1 is '扩展字段'; comment on column sys_oss_config.create_by is '创建者'; comment on column sys_oss_config.create_time is '创建时间'; comment on column sys_oss_config.update_by is '更新者'; comment on column sys_oss_config.update_time is '更新时间'; comment on column sys_oss_config.remark is '备注'; insert into sys_oss_config values (1, 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', '127.0.0.1:9000', '','N', '', '1', '0', '', 'admin', now(), 'admin', now(), null); insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', 'admin', now(), 'admin', now(), null); insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', 'admin', now(), 'admin', now(), null); insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', 'admin', now(), 'admin', now(), null); insert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', 'admin', now(), 'admin', now(), NULL); -- 字符串自动转时间 避免框架时间查询报错问题 create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$ select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss'); $$ language sql strict ; create cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT; ================================================ FILE: script/sql/postgres/postgres_test.sql ================================================ DROP TABLE if EXISTS test_demo; create table if not exists test_demo ( id int8, dept_id int8, user_id int8, order_num int4 default 0, test_key varchar(255), value varchar(255), version int4 default 0, create_time timestamp, create_by varchar(64), update_time timestamp, update_by varchar(64), del_flag int4 default 0 ); comment on table test_demo is '测试单表'; comment on column test_demo.id is '主键'; comment on column test_demo.dept_id is '部门id'; comment on column test_demo.user_id is '用户id'; comment on column test_demo.order_num is '排序号'; comment on column test_demo.test_key is 'key键'; comment on column test_demo.value is '值'; comment on column test_demo.version is '版本'; comment on column test_demo.create_time is '创建时间'; comment on column test_demo.create_by is '创建人'; comment on column test_demo.update_time is '更新时间'; comment on column test_demo.update_by is '更新人'; comment on column test_demo.del_flag is '删除标志'; DROP TABLE if EXISTS test_tree; create table if not exists test_tree ( id int8, parent_id int8 default 0, dept_id int8, user_id int8, tree_name varchar(255), version int4 default 0, create_time timestamp, create_by varchar(64), update_time timestamp, update_by varchar(64), del_flag integer default 0 ); comment on table test_tree is '测试树表'; comment on column test_tree.id is '主键'; comment on column test_tree.parent_id is '父id'; comment on column test_tree.dept_id is '部门id'; comment on column test_tree.user_id is '用户id'; comment on column test_tree.tree_name is '值'; comment on column test_tree.version is '版本'; comment on column test_tree.create_time is '创建时间'; comment on column test_tree.create_by is '创建人'; comment on column test_tree.update_time is '更新时间'; comment on column test_tree.update_by is '更新人'; comment on column test_tree.del_flag is '删除标志'; INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 'admin', now(), 'test', now(), NULL); INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 'admin', now(), 'test1', now(), NULL); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', now(), NULL, NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', now(), '', NULL, '测试单表菜单'); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', now(), '', NULL, '测试树表菜单'); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', now(), '', NULL, ''); INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 't', 't', '0', '0', 'admin', now(), 'admin', NULL, NULL); INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 't', 't', '0', '0', 'admin', now(), 'admin', NULL, NULL); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511); INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3); INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, now(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, now(), 'admin', NULL, NULL, 0); ================================================ FILE: script/sql/ry_vue_4.X.sql ================================================ -- ---------------------------- -- 1、部门表 -- ---------------------------- drop table if exists sys_dept; create table sys_dept ( dept_id bigint(20) not null comment '部门id', parent_id bigint(20) default 0 comment '父部门id', ancestors varchar(500) default '' comment '祖级列表', dept_name varchar(30) default '' comment '部门名称', order_num int(4) default 0 comment '显示顺序', leader varchar(20) default null comment '负责人', phone varchar(11) default null comment '联系电话', email varchar(50) default null comment '邮箱', status char(1) default '0' comment '部门状态(0正常 1停用)', del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', primary key (dept_id) ) engine=innodb comment = '部门表'; -- ---------------------------- -- 初始化-部门表数据 -- ---------------------------- insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); -- ---------------------------- -- 2、用户信息表 -- ---------------------------- drop table if exists sys_user; create table sys_user ( user_id bigint(20) not null comment '用户ID', dept_id bigint(20) default null comment '部门ID', user_name varchar(30) not null comment '用户账号', nick_name varchar(30) not null comment '用户昵称', user_type varchar(10) default 'sys_user' comment '用户类型(sys_user系统用户)', email varchar(50) default '' comment '用户邮箱', phonenumber varchar(11) default '' comment '手机号码', sex char(1) default '0' comment '用户性别(0男 1女 2未知)', avatar varchar(100) default '' comment '头像地址', password varchar(100) default '' comment '密码', status char(1) default '0' comment '帐号状态(0正常 1停用)', del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', login_ip varchar(128) default '' comment '最后登录IP', login_date datetime comment '最后登录时间', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (user_id) ) engine=innodb comment = '用户信息表'; -- ---------------------------- -- 初始化-用户信息表数据 -- ---------------------------- insert into sys_user values(1, 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员'); insert into sys_user values(2, 105, 'lionli', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员'); -- ---------------------------- -- 3、岗位信息表 -- ---------------------------- drop table if exists sys_post; create table sys_post ( post_id bigint(20) not null comment '岗位ID', post_code varchar(64) not null comment '岗位编码', post_name varchar(50) not null comment '岗位名称', post_sort int(4) not null comment '显示顺序', status char(1) not null comment '状态(0正常 1停用)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (post_id) ) engine=innodb comment = '岗位信息表'; -- ---------------------------- -- 初始化-岗位信息表数据 -- ---------------------------- insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, ''); insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, ''); insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, ''); insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, ''); -- ---------------------------- -- 4、角色信息表 -- ---------------------------- drop table if exists sys_role; create table sys_role ( role_id bigint(20) not null comment '角色ID', role_name varchar(30) not null comment '角色名称', role_key varchar(100) not null comment '角色权限字符串', role_sort int(4) not null comment '显示顺序', data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', dept_check_strictly tinyint(1) default 1 comment '部门树选择项是否关联显示', status char(1) not null comment '角色状态(0正常 1停用)', del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (role_id) ) engine=innodb comment = '角色信息表'; -- ---------------------------- -- 初始化-角色信息表数据 -- ---------------------------- insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员'); insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate(), '', null, '普通角色'); -- ---------------------------- -- 5、菜单权限表 -- ---------------------------- drop table if exists sys_menu; create table sys_menu ( menu_id bigint(20) not null comment '菜单ID', menu_name varchar(50) not null comment '菜单名称', parent_id bigint(20) default 0 comment '父菜单ID', order_num int(4) default 0 comment '显示顺序', path varchar(200) default '' comment '路由地址', component varchar(255) default null comment '组件路径', query_param varchar(255) default null comment '路由参数', is_frame int(1) default 1 comment '是否为外链(0是 1否)', is_cache int(1) default 0 comment '是否缓存(0缓存 1不缓存)', menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)', visible char(1) default 0 comment '显示状态(0显示 1隐藏)', status char(1) default 0 comment '菜单状态(0正常 1停用)', perms varchar(100) default null comment '权限标识', icon varchar(100) default '#' comment '菜单图标', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default '' comment '备注', primary key (menu_id) ) engine=innodb comment = '菜单权限表'; -- ---------------------------- -- 初始化-菜单信息表数据 -- ---------------------------- -- 一级菜单 insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); insert into sys_menu values('4', 'PLUS官网', '0', '4', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, 'RuoYi-Vue-Plus官网地址'); -- 二级菜单 insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单'); insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate(), '', null, '菜单管理菜单'); insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate(), '', null, '部门管理菜单'); insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate(), '', null, '岗位管理菜单'); insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate(), '', null, '字典管理菜单'); insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate(), '', null, '参数设置菜单'); insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate(), '', null, '通知公告菜单'); insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate(), '', null, '日志管理菜单'); insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate(), '', null, '在线用户菜单'); insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单'); insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate(), '', null, '缓存监控菜单'); insert into sys_menu values('114', '表单构建', '3', '1', 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate(), '', null, '表单构建菜单'); insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate(), '', null, '代码生成菜单'); -- springboot-admin监控 insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 'admin', sysdate(), '', null, 'Admin监控菜单'); -- oss菜单 insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 'admin', sysdate(), '', null, '文件管理菜单'); -- xxl-job-admin控制台 insert into sys_menu values('120', '任务调度中心', '2', '5', 'XxlJob', 'monitor/xxljob/index', '', 1, 0, 'C', '0', '0', 'monitor:xxljob:list', 'job', 'admin', sysdate(), '', null, 'Xxl-Job控制台菜单'); -- 三级菜单 insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate(), '', null, '操作日志菜单'); insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate(), '', null, '登录日志菜单'); -- 用户管理按钮 insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, ''); -- 角色管理按钮 insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate(), '', null, ''); -- 菜单管理按钮 insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate(), '', null, ''); -- 部门管理按钮 insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate(), '', null, ''); -- 岗位管理按钮 insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate(), '', null, ''); -- 字典管理按钮 insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate(), '', null, ''); -- 参数设置按钮 insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate(), '', null, ''); -- 通知公告按钮 insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate(), '', null, ''); -- 操作日志按钮 insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, ''); -- 登录日志按钮 insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, ''); -- 在线用户按钮 insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, ''); -- 代码生成按钮 insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, ''); -- oss相关按钮 insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:add', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:edit', '#', 'admin', sysdate(), '', null, ''); -- ---------------------------- -- 6、用户和角色关联表 用户N-1角色 -- ---------------------------- drop table if exists sys_user_role; create table sys_user_role ( user_id bigint(20) not null comment '用户ID', role_id bigint(20) not null comment '角色ID', primary key(user_id, role_id) ) engine=innodb comment = '用户和角色关联表'; -- ---------------------------- -- 初始化-用户和角色关联表数据 -- ---------------------------- insert into sys_user_role values ('1', '1'); insert into sys_user_role values ('2', '2'); -- ---------------------------- -- 7、角色和菜单关联表 角色1-N菜单 -- ---------------------------- drop table if exists sys_role_menu; create table sys_role_menu ( role_id bigint(20) not null comment '角色ID', menu_id bigint(20) not null comment '菜单ID', primary key(role_id, menu_id) ) engine=innodb comment = '角色和菜单关联表'; -- ---------------------------- -- 初始化-角色和菜单关联表数据 -- ---------------------------- insert into sys_role_menu values ('2', '1'); insert into sys_role_menu values ('2', '2'); insert into sys_role_menu values ('2', '3'); insert into sys_role_menu values ('2', '4'); insert into sys_role_menu values ('2', '100'); insert into sys_role_menu values ('2', '101'); insert into sys_role_menu values ('2', '102'); insert into sys_role_menu values ('2', '103'); insert into sys_role_menu values ('2', '104'); insert into sys_role_menu values ('2', '105'); insert into sys_role_menu values ('2', '106'); insert into sys_role_menu values ('2', '107'); insert into sys_role_menu values ('2', '108'); insert into sys_role_menu values ('2', '109'); insert into sys_role_menu values ('2', '110'); insert into sys_role_menu values ('2', '111'); insert into sys_role_menu values ('2', '112'); insert into sys_role_menu values ('2', '113'); insert into sys_role_menu values ('2', '114'); insert into sys_role_menu values ('2', '115'); insert into sys_role_menu values ('2', '116'); insert into sys_role_menu values ('2', '500'); insert into sys_role_menu values ('2', '501'); insert into sys_role_menu values ('2', '1000'); insert into sys_role_menu values ('2', '1001'); insert into sys_role_menu values ('2', '1002'); insert into sys_role_menu values ('2', '1003'); insert into sys_role_menu values ('2', '1004'); insert into sys_role_menu values ('2', '1005'); insert into sys_role_menu values ('2', '1006'); insert into sys_role_menu values ('2', '1007'); insert into sys_role_menu values ('2', '1008'); insert into sys_role_menu values ('2', '1009'); insert into sys_role_menu values ('2', '1010'); insert into sys_role_menu values ('2', '1011'); insert into sys_role_menu values ('2', '1012'); insert into sys_role_menu values ('2', '1013'); insert into sys_role_menu values ('2', '1014'); insert into sys_role_menu values ('2', '1015'); insert into sys_role_menu values ('2', '1016'); insert into sys_role_menu values ('2', '1017'); insert into sys_role_menu values ('2', '1018'); insert into sys_role_menu values ('2', '1019'); insert into sys_role_menu values ('2', '1020'); insert into sys_role_menu values ('2', '1021'); insert into sys_role_menu values ('2', '1022'); insert into sys_role_menu values ('2', '1023'); insert into sys_role_menu values ('2', '1024'); insert into sys_role_menu values ('2', '1025'); insert into sys_role_menu values ('2', '1026'); insert into sys_role_menu values ('2', '1027'); insert into sys_role_menu values ('2', '1028'); insert into sys_role_menu values ('2', '1029'); insert into sys_role_menu values ('2', '1030'); insert into sys_role_menu values ('2', '1031'); insert into sys_role_menu values ('2', '1032'); insert into sys_role_menu values ('2', '1033'); insert into sys_role_menu values ('2', '1034'); insert into sys_role_menu values ('2', '1035'); insert into sys_role_menu values ('2', '1036'); insert into sys_role_menu values ('2', '1037'); insert into sys_role_menu values ('2', '1038'); insert into sys_role_menu values ('2', '1039'); insert into sys_role_menu values ('2', '1040'); insert into sys_role_menu values ('2', '1041'); insert into sys_role_menu values ('2', '1042'); insert into sys_role_menu values ('2', '1043'); insert into sys_role_menu values ('2', '1044'); insert into sys_role_menu values ('2', '1045'); insert into sys_role_menu values ('2', '1050'); insert into sys_role_menu values ('2', '1046'); insert into sys_role_menu values ('2', '1047'); insert into sys_role_menu values ('2', '1048'); insert into sys_role_menu values ('2', '1055'); insert into sys_role_menu values ('2', '1056'); insert into sys_role_menu values ('2', '1057'); insert into sys_role_menu values ('2', '1058'); insert into sys_role_menu values ('2', '1059'); insert into sys_role_menu values ('2', '1060'); -- ---------------------------- -- 8、角色和部门关联表 角色1-N部门 -- ---------------------------- drop table if exists sys_role_dept; create table sys_role_dept ( role_id bigint(20) not null comment '角色ID', dept_id bigint(20) not null comment '部门ID', primary key(role_id, dept_id) ) engine=innodb comment = '角色和部门关联表'; -- ---------------------------- -- 初始化-角色和部门关联表数据 -- ---------------------------- insert into sys_role_dept values ('2', '100'); insert into sys_role_dept values ('2', '101'); insert into sys_role_dept values ('2', '105'); -- ---------------------------- -- 9、用户与岗位关联表 用户1-N岗位 -- ---------------------------- drop table if exists sys_user_post; create table sys_user_post ( user_id bigint(20) not null comment '用户ID', post_id bigint(20) not null comment '岗位ID', primary key (user_id, post_id) ) engine=innodb comment = '用户与岗位关联表'; -- ---------------------------- -- 初始化-用户与岗位关联表数据 -- ---------------------------- insert into sys_user_post values ('1', '1'); insert into sys_user_post values ('2', '2'); -- ---------------------------- -- 10、操作日志记录 -- ---------------------------- drop table if exists sys_oper_log; create table sys_oper_log ( oper_id bigint(20) not null comment '日志主键', title varchar(50) default '' comment '模块标题', business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', method varchar(100) default '' comment '方法名称', request_method varchar(10) default '' comment '请求方式', operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', oper_name varchar(50) default '' comment '操作人员', dept_name varchar(50) default '' comment '部门名称', oper_url varchar(255) default '' comment '请求URL', oper_ip varchar(128) default '' comment '主机地址', oper_location varchar(255) default '' comment '操作地点', oper_param varchar(2000) default '' comment '请求参数', json_result varchar(2000) default '' comment '返回参数', status int(1) default 0 comment '操作状态(0正常 1异常)', error_msg varchar(2000) default '' comment '错误消息', oper_time datetime comment '操作时间', primary key (oper_id), key idx_sys_oper_log_bt (business_type), key idx_sys_oper_log_s (status), key idx_sys_oper_log_ot (oper_time) ) engine=innodb comment = '操作日志记录'; -- ---------------------------- -- 11、字典类型表 -- ---------------------------- drop table if exists sys_dict_type; create table sys_dict_type ( dict_id bigint(20) not null comment '字典主键', dict_name varchar(100) default '' comment '字典名称', dict_type varchar(100) default '' comment '字典类型', status char(1) default '0' comment '状态(0正常 1停用)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (dict_id), unique (dict_type) ) engine=innodb comment = '字典类型表'; insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate(), '', null, '用户性别列表'); insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate(), '', null, '菜单状态列表'); insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate(), '', null, '系统开关列表'); insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate(), '', null, '系统是否列表'); insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate(), '', null, '通知类型列表'); insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate(), '', null, '通知状态列表'); insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate(), '', null, '操作类型列表'); insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate(), '', null, '登录状态列表'); -- ---------------------------- -- 12、字典数据表 -- ---------------------------- drop table if exists sys_dict_data; create table sys_dict_data ( dict_code bigint(20) not null comment '字典编码', dict_sort int(4) default 0 comment '字典排序', dict_label varchar(100) default '' comment '字典标签', dict_value varchar(100) default '' comment '字典键值', dict_type varchar(100) default '' comment '字典类型', css_class varchar(100) default null comment '样式属性(其他样式扩展)', list_class varchar(100) default null comment '表格回显样式', is_default char(1) default 'N' comment '是否默认(Y是 N否)', status char(1) default '0' comment '状态(0正常 1停用)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (dict_code) ) engine=innodb comment = '字典数据表'; insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate(), '', null, '性别男'); insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别女'); insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别未知'); insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单'); insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '隐藏菜单'); insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是'); insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '系统默认否'); insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知'); insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate(), '', null, '公告'); insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '关闭状态'); insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作'); insert into sys_dict_data values(18, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作'); insert into sys_dict_data values(19, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作'); insert into sys_dict_data values(20, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作'); insert into sys_dict_data values(21, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作'); insert into sys_dict_data values(22, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作'); insert into sys_dict_data values(23, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作'); insert into sys_dict_data values(24, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作'); insert into sys_dict_data values(25, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作'); insert into sys_dict_data values(26, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作'); insert into sys_dict_data values(27, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态'); insert into sys_dict_data values(28, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); -- ---------------------------- -- 13、参数配置表 -- ---------------------------- drop table if exists sys_config; create table sys_config ( config_id bigint(20) not null comment '参数主键', config_name varchar(100) default '' comment '参数名称', config_key varchar(100) default '' comment '参数键名', config_value varchar(500) default '' comment '参数键值', config_type char(1) default 'N' comment '系统内置(Y是 N否)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (config_id) ) engine=innodb comment = '参数配置表'; insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate(), '', null, '初始化密码 123456' ); insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' ); insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)'); insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)'); insert into sys_config values(11, 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 'admin', sysdate(), '', null, 'true:开启, false:关闭'); -- ---------------------------- -- 14、系统访问记录 -- ---------------------------- drop table if exists sys_logininfor; create table sys_logininfor ( info_id bigint(20) not null comment '访问ID', user_name varchar(50) default '' comment '用户账号', ipaddr varchar(128) default '' comment '登录IP地址', login_location varchar(255) default '' comment '登录地点', browser varchar(50) default '' comment '浏览器类型', os varchar(50) default '' comment '操作系统', status char(1) default '0' comment '登录状态(0成功 1失败)', msg varchar(255) default '' comment '提示消息', login_time datetime comment '访问时间', primary key (info_id), key idx_sys_logininfor_s (status), key idx_sys_logininfor_lt (login_time) ) engine=innodb comment = '系统访问记录'; -- ---------------------------- -- 17、通知公告表 -- ---------------------------- drop table if exists sys_notice; create table sys_notice ( notice_id bigint(20) not null comment '公告ID', notice_title varchar(50) not null comment '公告标题', notice_type char(1) not null comment '公告类型(1通知 2公告)', notice_content longblob default null comment '公告内容', status char(1) default '0' comment '公告状态(0正常 1关闭)', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(255) default null comment '备注', primary key (notice_id) ) engine=innodb comment = '通知公告表'; -- ---------------------------- -- 初始化-公告信息表数据 -- ---------------------------- insert into sys_notice values('1', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员'); insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate(), '', null, '管理员'); -- ---------------------------- -- 18、代码生成业务表 -- ---------------------------- drop table if exists gen_table; create table gen_table ( table_id bigint(20) not null comment '编号', table_name varchar(200) default '' comment '表名称', table_comment varchar(500) default '' comment '表描述', sub_table_name varchar(64) default null comment '关联子表的表名', sub_table_fk_name varchar(64) default null comment '子表关联的外键名', class_name varchar(100) default '' comment '实体类名称', tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', package_name varchar(100) comment '生成包路径', module_name varchar(30) comment '生成模块名', business_name varchar(30) comment '生成业务名', function_name varchar(50) comment '生成功能名', function_author varchar(50) comment '生成功能作者', gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)', gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)', options varchar(1000) comment '其它生成选项', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', remark varchar(500) default null comment '备注', primary key (table_id) ) engine=innodb comment = '代码生成业务表'; -- ---------------------------- -- 19、代码生成业务表字段 -- ---------------------------- drop table if exists gen_table_column; create table gen_table_column ( column_id bigint(20) not null comment '编号', table_id bigint(20) comment '归属表编号', column_name varchar(200) comment '列名称', column_comment varchar(500) comment '列描述', column_type varchar(100) comment '列类型', java_type varchar(500) comment 'JAVA类型', java_field varchar(200) comment 'JAVA字段名', is_pk char(1) comment '是否主键(1是)', is_increment char(1) comment '是否自增(1是)', is_required char(1) comment '是否必填(1是)', is_insert char(1) comment '是否为插入字段(1是)', is_edit char(1) comment '是否编辑字段(1是)', is_list char(1) comment '是否列表字段(1是)', is_query char(1) comment '是否查询字段(1是)', query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', dict_type varchar(200) default '' comment '字典类型', sort int comment '排序', create_by varchar(64) default '' comment '创建者', create_time datetime comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime comment '更新时间', primary key (column_id) ) engine=innodb comment = '代码生成业务表字段'; -- ---------------------------- -- OSS对象存储表 -- ---------------------------- drop table if exists sys_oss; create table sys_oss ( oss_id bigint(20) not null comment '对象存储主键', file_name varchar(255) not null default '' comment '文件名', original_name varchar(255) not null default '' comment '原名', file_suffix varchar(10) not null default '' comment '文件后缀名', url varchar(500) not null comment 'URL地址', create_time datetime default null comment '创建时间', create_by varchar(64) default '' comment '上传人', update_time datetime default null comment '更新时间', update_by varchar(64) default '' comment '更新人', service varchar(20) not null default 'minio' comment '服务商', primary key (oss_id) ) engine=innodb comment ='OSS对象存储表'; -- ---------------------------- -- OSS对象存储动态配置表 -- ---------------------------- drop table if exists sys_oss_config; create table sys_oss_config ( oss_config_id bigint(20) not null comment '主建', config_key varchar(20) not null default '' comment '配置key', access_key varchar(255) default '' comment 'accessKey', secret_key varchar(255) default '' comment '秘钥', bucket_name varchar(255) default '' comment '桶名称', prefix varchar(255) default '' comment '前缀', endpoint varchar(255) default '' comment '访问站点', domain varchar(255) default '' comment '自定义域名', is_https char(1) default 'N' comment '是否https(Y=是,N=否)', region varchar(255) default '' comment '域', access_policy char(1) not null default '1' comment '桶权限类型(0=private 1=public 2=custom)', status char(1) default '1' comment '是否默认(0=是,1=否)', ext1 varchar(255) default '' comment '扩展字段', create_by varchar(64) default '' comment '创建者', create_time datetime default null comment '创建时间', update_by varchar(64) default '' comment '更新者', update_time datetime default null comment '更新时间', remark varchar(500) default null comment '备注', primary key (oss_config_id) ) engine=innodb comment='对象存储配置表'; insert into sys_oss_config values (1, 'minio', 'ruoyi', 'ruoyi123', 'ruoyi', '', '127.0.0.1:9000', '','N', '', '1' ,'0', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (2, 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'ruoyi-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); insert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', '','N', '', '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL); ================================================ FILE: script/sql/sqlserver/sqlserver_ry_vue_4.X.sql ================================================ CREATE TABLE gen_table ( table_id bigint NOT NULL, table_name nvarchar(200) DEFAULT '' NULL, table_comment nvarchar(500) DEFAULT '' NULL, sub_table_name nvarchar(64) NULL, sub_table_fk_name nvarchar(64) NULL, class_name nvarchar(100) DEFAULT '' NULL, tpl_category nvarchar(200) DEFAULT ('crud') NULL, package_name nvarchar(100) NULL, module_name nvarchar(30) NULL, business_name nvarchar(30) NULL, function_name nvarchar(50) NULL, function_author nvarchar(50) NULL, gen_type nchar(1) DEFAULT ('0') NULL, gen_path nvarchar(200) DEFAULT ('/') NULL, options nvarchar(1000) NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__gen_tabl__B21E8F2427725F8A PRIMARY KEY CLUSTERED (table_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'编号' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'table_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'表名称' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'table_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'表描述' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'table_comment' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'关联子表的表名' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'sub_table_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'子表关联的外键名' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'sub_table_fk_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'实体类名称' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'class_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'使用的模板(crud单表操作 tree树表操作)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'tpl_category' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'生成包路径' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'package_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'生成模块名' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'module_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'生成业务名' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'business_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'生成功能名' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'function_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'生成功能作者' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'function_author' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'生成代码方式(0zip压缩包 1自定义路径)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'gen_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'生成路径(不填默认项目路径)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'gen_path' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'其它生成选项' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'options' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'代码生成业务表' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table' GO CREATE TABLE gen_table_column ( column_id bigint NOT NULL, table_id bigint NULL, column_name nvarchar(200) NULL, column_comment nvarchar(500) NULL, column_type nvarchar(100) NULL, java_type nvarchar(500) NULL, java_field nvarchar(200) NULL, is_pk nchar(1) NULL, is_increment nchar(1) NULL, is_required nchar(1) NULL, is_insert nchar(1) NULL, is_edit nchar(1) NULL, is_list nchar(1) NULL, is_query nchar(1) NULL, query_type nvarchar(200) DEFAULT ('EQ') NULL, html_type nvarchar(200) NULL, dict_type nvarchar(200) DEFAULT '' NULL, sort int NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, CONSTRAINT PK__gen_tabl__E301851F2E68B4E8 PRIMARY KEY CLUSTERED (column_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'编号' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'column_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'归属表编号' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'table_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'列名称' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'column_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'列描述' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'column_comment' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'列类型' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'column_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'JAVA类型' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'java_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'JAVA字段名' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'java_field' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否主键(1是)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'is_pk' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否自增(1是)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'is_increment' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否必填(1是)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'is_required' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否为插入字段(1是)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'is_insert' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否编辑字段(1是)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'is_edit' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否列表字段(1是)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'is_list' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否查询字段(1是)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'is_query' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'查询方式(等于、不等于、大于、小于、范围)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'query_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'html_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典类型' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'dict_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'排序' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'sort' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'代码生成业务表字段' , 'SCHEMA', N'dbo', 'TABLE', N'gen_table_column' GO CREATE TABLE sys_config ( config_id bigint NOT NULL, config_name nvarchar(100) DEFAULT '' NULL, config_key nvarchar(100) DEFAULT '' NULL, config_value nvarchar(500) DEFAULT '' NULL, config_type nchar(1) DEFAULT ('N') NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__sys_conf__4AD1BFF182643682 PRIMARY KEY CLUSTERED (config_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'参数主键' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'config_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'参数名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'config_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'参数键名' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'config_key' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'参数键值' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'config_value' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'系统内置(Y是 N否)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'config_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'参数配置表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_config' GO INSERT sys_config VALUES (1, N'主框架页-默认皮肤样式名称', N'sys.index.skinName', N'skin-blue', N'Y', N'admin', getdate(), N'', NULL, N'蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow') GO INSERT sys_config VALUES (2, N'用户管理-账号初始密码', N'sys.user.initPassword', N'123456', N'Y', N'admin', getdate(), N'', NULL, N'初始化密码 123456') GO INSERT sys_config VALUES (3, N'主框架页-侧边栏主题', N'sys.index.sideTheme', N'theme-dark', N'Y', N'admin', getdate(), N'', NULL, N'深色主题theme-dark,浅色主题theme-light') GO INSERT sys_config VALUES (4, N'账号自助-验证码开关', N'sys.account.captchaEnabled', N'true', N'Y', N'admin', getdate(), N'', NULL, N'是否开启验证码功能(true开启,false关闭)') GO INSERT sys_config VALUES (5, N'账号自助-是否开启用户注册功能', N'sys.account.registerUser', N'false', N'Y', N'admin', getdate(), N'', NULL, N'是否开启注册用户功能(true开启,false关闭)') GO INSERT sys_config VALUES (11, N'OSS预览列表资源开关', N'sys.oss.previewListResource', N'true', N'Y', N'admin', getdate(), N'', null, N'true:开启, false:关闭'); GO CREATE TABLE sys_dept ( dept_id bigint NOT NULL, parent_id bigint DEFAULT ((0)) NULL, ancestors nvarchar(500)DEFAULT '' NULL, dept_name nvarchar(30) DEFAULT '' NULL, order_num int DEFAULT ((0)) NULL, leader nvarchar(20) NULL, phone nvarchar(11) NULL, email nvarchar(50) NULL, status nchar(1) DEFAULT ('0') NULL, del_flag nchar(1) DEFAULT ('0') NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, CONSTRAINT PK__sys_dept__DCA659747DE13804 PRIMARY KEY CLUSTERED (dept_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门id' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'dept_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'父部门id' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'parent_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'祖级列表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'ancestors' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'dept_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'显示顺序' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'order_num' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'负责人' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'leader' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'联系电话' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'phone' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'邮箱' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'email' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门状态(0正常 1停用)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'删除标志(0代表存在 2代表删除)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'del_flag' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dept' GO INSERT sys_dept VALUES (100, 0, N'0', N'若依科技', 0, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (101, 100, N'0,100', N'深圳总公司', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (102, 100, N'0,100', N'长沙分公司', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (103, 101, N'0,100,101', N'研发部门', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (104, 101, N'0,100,101', N'市场部门', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (105, 101, N'0,100,101', N'测试部门', 3, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (106, 101, N'0,100,101', N'财务部门', 4, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (107, 101, N'0,100,101', N'运维部门', 5, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (108, 102, N'0,100,102', N'市场部门', 1, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO INSERT sys_dept VALUES (109, 102, N'0,100,102', N'财务部门', 2, N'若依', N'15888888888', N'ry@qq.com', N'0', N'0', N'admin', getdate(), N'', NULL) GO CREATE TABLE sys_dict_data ( dict_code bigint NOT NULL, dict_sort int DEFAULT ((0)) NULL, dict_label nvarchar(100) DEFAULT '' NULL, dict_value nvarchar(100) DEFAULT '' NULL, dict_type nvarchar(100) DEFAULT '' NULL, css_class nvarchar(100) NULL, list_class nvarchar(100) NULL, is_default nchar(1) DEFAULT ('N') NULL, status nchar(1) DEFAULT ('0') NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__sys_dict__19CBC34B661AF3B3 PRIMARY KEY CLUSTERED (dict_code) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典编码' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'dict_code' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典排序' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'dict_sort' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典标签' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'dict_label' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典键值' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'dict_value' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典类型' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'dict_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'样式属性(其他样式扩展)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'css_class' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'表格回显样式' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'list_class' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否默认(Y是 N否)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'is_default' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'状态(0正常 1停用)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典数据表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_data' GO INSERT sys_dict_data VALUES (1, 1, N'男', N'0', N'sys_user_sex', N'', N'', N'Y', N'0', N'admin', getdate(), N'', NULL, N'性别男') GO INSERT sys_dict_data VALUES (2, 2, N'女', N'1', N'sys_user_sex', N'', N'', N'N', N'0', N'admin', getdate(), N'', NULL, N'性别女') GO INSERT sys_dict_data VALUES (3, 3, N'未知', N'2', N'sys_user_sex', N'', N'', N'N', N'0', N'admin', getdate(), N'', NULL, N'性别未知') GO INSERT sys_dict_data VALUES (4, 1, N'显示', N'0', N'sys_show_hide', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'显示菜单') GO INSERT sys_dict_data VALUES (5, 2, N'隐藏', N'1', N'sys_show_hide', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'隐藏菜单') GO INSERT sys_dict_data VALUES (6, 1, N'正常', N'0', N'sys_normal_disable', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'正常状态') GO INSERT sys_dict_data VALUES (7, 2, N'停用', N'1', N'sys_normal_disable', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'停用状态') GO INSERT sys_dict_data VALUES (12, 1, N'是', N'Y', N'sys_yes_no', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'系统默认是') GO INSERT sys_dict_data VALUES (13, 2, N'否', N'N', N'sys_yes_no', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'系统默认否') GO INSERT sys_dict_data VALUES (14, 1, N'通知', N'1', N'sys_notice_type', N'', N'warning', N'Y', N'0', N'admin', getdate(), N'', NULL, N'通知') GO INSERT sys_dict_data VALUES (15, 2, N'公告', N'2', N'sys_notice_type', N'', N'success', N'N', N'0', N'admin', getdate(), N'', NULL, N'公告') GO INSERT sys_dict_data VALUES (16, 1, N'正常', N'0', N'sys_notice_status', N'', N'primary', N'Y', N'0', N'admin', getdate(), N'', NULL, N'正常状态') GO INSERT sys_dict_data VALUES (17, 2, N'关闭', N'1', N'sys_notice_status', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'关闭状态') GO INSERT sys_dict_data VALUES (29, 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'其他操作'); GO INSERT sys_dict_data VALUES (18, 1, N'新增', N'1', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'新增操作') GO INSERT sys_dict_data VALUES (19, 2, N'修改', N'2', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'修改操作') GO INSERT sys_dict_data VALUES (20, 3, N'删除', N'3', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'删除操作') GO INSERT sys_dict_data VALUES (21, 4, N'授权', N'4', N'sys_oper_type', N'', N'primary', N'N', N'0', N'admin', getdate(), N'', NULL, N'授权操作') GO INSERT sys_dict_data VALUES (22, 5, N'导出', N'5', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'导出操作') GO INSERT sys_dict_data VALUES (23, 6, N'导入', N'6', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'导入操作') GO INSERT sys_dict_data VALUES (24, 7, N'强退', N'7', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'强退操作') GO INSERT sys_dict_data VALUES (25, 8, N'生成代码', N'8', N'sys_oper_type', N'', N'warning', N'N', N'0', N'admin', getdate(), N'', NULL, N'生成操作') GO INSERT sys_dict_data VALUES (26, 9, N'清空数据', N'9', N'sys_oper_type', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'清空操作') GO INSERT sys_dict_data VALUES (27, 1, N'成功', N'0', N'sys_common_status', N'', N'primary', N'N', N'0', N'admin', getdate(), N'', NULL, N'正常状态') GO INSERT sys_dict_data VALUES (28, 2, N'失败', N'1', N'sys_common_status', N'', N'danger', N'N', N'0', N'admin', getdate(), N'', NULL, N'停用状态') GO CREATE TABLE sys_dict_type ( dict_id bigint NOT NULL, dict_name nvarchar(100) DEFAULT '' NULL, dict_type nvarchar(100) DEFAULT '' NULL, status nchar(1) DEFAULT ('0') NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__sys_dict__3BD4186C409C5391 PRIMARY KEY CLUSTERED (dict_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO CREATE NONCLUSTERED INDEX sys_dict_type_index1 ON sys_dict_type (dict_type) GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典主键' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'dict_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'dict_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典类型' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'dict_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'状态(0正常 1停用)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'字典类型表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_dict_type' GO INSERT sys_dict_type VALUES (1, N'用户性别', N'sys_user_sex', N'0', N'admin', getdate(), N'', NULL, N'用户性别列表') GO INSERT sys_dict_type VALUES (2, N'菜单状态', N'sys_show_hide', N'0', N'admin', getdate(), N'', NULL, N'菜单状态列表') GO INSERT sys_dict_type VALUES (3, N'系统开关', N'sys_normal_disable', N'0', N'admin', getdate(), N'', NULL, N'系统开关列表') GO INSERT sys_dict_type VALUES (6, N'系统是否', N'sys_yes_no', N'0', N'admin', getdate(), N'', NULL, N'系统是否列表') GO INSERT sys_dict_type VALUES (7, N'通知类型', N'sys_notice_type', N'0', N'admin', getdate(), N'', NULL, N'通知类型列表') GO INSERT sys_dict_type VALUES (8, N'通知状态', N'sys_notice_status', N'0', N'admin', getdate(), N'', NULL, N'通知状态列表') GO INSERT sys_dict_type VALUES (9, N'操作类型', N'sys_oper_type', N'0', N'admin', getdate(), N'', NULL, N'操作类型列表') GO INSERT sys_dict_type VALUES (10, N'系统状态', N'sys_common_status', N'0', N'admin', getdate(), N'', NULL, N'登录状态列表') GO CREATE TABLE sys_logininfor ( info_id bigint NOT NULL, user_name nvarchar(50) DEFAULT '' NULL, ipaddr nvarchar(128) DEFAULT '' NULL, login_location nvarchar(255) DEFAULT '' NULL, browser nvarchar(50) DEFAULT '' NULL, os nvarchar(50) DEFAULT '' NULL, status nchar(1) DEFAULT ('0') NULL, msg nvarchar(255) DEFAULT '' NULL, login_time datetime2(7) NULL, CONSTRAINT PK__sys_logi__3D8A9C1A1854AE10 PRIMARY KEY CLUSTERED (info_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO CREATE NONCLUSTERED INDEX idx_sys_logininfor_s ON sys_logininfor (status) GO CREATE NONCLUSTERED INDEX idx_sys_logininfor_lt ON sys_logininfor (login_time) GO EXEC sys.sp_addextendedproperty 'MS_Description', N'访问ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'info_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户账号' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'user_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'登录IP地址' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'ipaddr' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'登录地点' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'login_location' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'浏览器类型' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'browser' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'操作系统' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'os' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'登录状态(0成功 1失败)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'提示消息' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'msg' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'访问时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor', 'COLUMN', N'login_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'系统访问记录' , 'SCHEMA', N'dbo', 'TABLE', N'sys_logininfor' GO CREATE TABLE sys_menu ( menu_id bigint NOT NULL, menu_name nvarchar(50) NOT NULL, parent_id bigint DEFAULT ((0)) NULL, order_num int DEFAULT ((0)) NULL, path nvarchar(200) DEFAULT '' NULL, component nvarchar(255) NULL, query_param nvarchar(255) NULL, is_frame int DEFAULT ((1)) NULL, is_cache int DEFAULT ((0)) NULL, menu_type nchar(1) DEFAULT '' NULL, visible nchar(1) DEFAULT ((0)) NULL, status nchar(1) DEFAULT ((0)) NULL, perms nvarchar(100) NULL, icon nvarchar(100) DEFAULT ('#') NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) DEFAULT '' NULL, CONSTRAINT PK__sys_menu__4CA0FADCF8545C58 PRIMARY KEY CLUSTERED (menu_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'menu_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'menu_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'父菜单ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'parent_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'显示顺序' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'order_num' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'路由地址' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'path' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'组件路径' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'component' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'路由参数' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'query_param' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否为外链(0是 1否)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'is_frame' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'是否缓存(0缓存 1不缓存)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'is_cache' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单类型(M目录 C菜单 F按钮)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'menu_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'显示状态(0显示 1隐藏)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'visible' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单状态(0正常 1停用)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'权限标识' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'perms' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单图标' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'icon' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单权限表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_menu' GO INSERT sys_menu VALUES (1, N'系统管理', 0, 1, N'system', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'system', N'admin', getdate(), N'', NULL, N'系统管理目录') GO INSERT sys_menu VALUES (2, N'系统监控', 0, 2, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', N'admin', getdate(), N'', NULL, N'系统监控目录') GO INSERT sys_menu VALUES (3, N'系统工具', 0, 3, N'tool', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'tool', N'admin', getdate(), N'', NULL, N'系统工具目录') GO INSERT sys_menu VALUES (4, N'PLUS官网', 0, 5, N'https://gitee.com/dromara/RuoYi-Vue-Plus', null, N'', 0, 0, N'M', N'0', N'0', N'', N'guide', N'admin', getdate(), N'', null, N'RuoYi-Vue-Plus官网地址'); GO INSERT sys_menu VALUES (100, N'用户管理', 1, 1, N'user', N'system/user/index', N'', 1, 0, N'C', N'0', N'0', N'system:user:list', N'user', N'admin', getdate(), N'', NULL, N'用户管理菜单') GO INSERT sys_menu VALUES (101, N'角色管理', 1, 2, N'role', N'system/role/index', N'', 1, 0, N'C', N'0', N'0', N'system:role:list', N'peoples', N'admin', getdate(), N'', NULL, N'角色管理菜单') GO INSERT sys_menu VALUES (102, N'菜单管理', 1, 3, N'menu', N'system/menu/index', N'', 1, 0, N'C', N'0', N'0', N'system:menu:list', N'tree-table', N'admin', getdate(), N'', NULL, N'菜单管理菜单') GO INSERT sys_menu VALUES (103, N'部门管理', 1, 4, N'dept', N'system/dept/index', N'', 1, 0, N'C', N'0', N'0', N'system:dept:list', N'tree', N'admin', getdate(), N'', NULL, N'部门管理菜单') GO INSERT sys_menu VALUES (104, N'岗位管理', 1, 5, N'post', N'system/post/index', N'', 1, 0, N'C', N'0', N'0', N'system:post:list', N'post', N'admin', getdate(), N'', NULL, N'岗位管理菜单') GO INSERT sys_menu VALUES (105, N'字典管理', 1, 6, N'dict', N'system/dict/index', N'', 1, 0, N'C', N'0', N'0', N'system:dict:list', N'dict', N'admin', getdate(), N'', NULL, N'字典管理菜单') GO INSERT sys_menu VALUES (106, N'参数设置', 1, 7, N'config', N'system/config/index', N'', 1, 0, N'C', N'0', N'0', N'system:config:list', N'edit', N'admin', getdate(), N'', NULL, N'参数设置菜单') GO INSERT sys_menu VALUES (107, N'通知公告', 1, 8, N'notice', N'system/notice/index', N'', 1, 0, N'C', N'0', N'0', N'system:notice:list', N'message', N'admin', getdate(), N'', NULL, N'通知公告菜单') GO INSERT sys_menu VALUES (108, N'日志管理', 1, 9, N'log', N'', N'', 1, 0, N'M', N'0', N'0', N'', N'log', N'admin', getdate(), N'', NULL, N'日志管理菜单') GO INSERT sys_menu VALUES (109, N'在线用户', 2, 1, N'online', N'monitor/online/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:online:list', N'online', N'admin', getdate(), N'', NULL, N'在线用户菜单') GO INSERT sys_menu VALUES (112, N'缓存列表', 2, 6, N'cacheList', N'monitor/cache/list', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis-list', N'admin', getdate(), N'', NULL, N'缓存列表菜单') GO INSERT sys_menu VALUES (113, N'缓存监控', 2, 5, N'cache', N'monitor/cache/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis', N'admin', getdate(), N'', NULL, N'缓存监控菜单') GO INSERT sys_menu VALUES (114, N'表单构建', 3, 1, N'build', N'tool/build/index', N'', 1, 0, N'C', N'0', N'0', N'tool:build:list', N'build', N'admin', getdate(), N'', NULL, N'表单构建菜单') GO INSERT sys_menu VALUES (115, N'代码生成', 3, 2, N'gen', N'tool/gen/index', N'', 1, 0, N'C', N'0', N'0', N'tool:gen:list', N'code', N'admin', getdate(), N'', NULL, N'代码生成菜单') GO INSERT sys_menu VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', N'admin', getdate(), N'', NULL, N'Admin监控菜单'); GO INSERT sys_menu VALUES (118, N'文件管理', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', N'admin', getdate(), N'', NULL, N'文件管理菜单'); GO INSERT sys_menu VALUES (120, N'任务调度中心', 2, 5, N'XxlJob', N'monitor/xxljob/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:xxljob:list', N'job', N'admin', getdate(), N'', NULL, N'Xxl-Job控制台菜单'); GO INSERT sys_menu VALUES (500, N'操作日志', 108, 1, N'operlog', N'monitor/operlog/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:operlog:list', N'form', N'admin', getdate(), N'', NULL, N'操作日志菜单') GO INSERT sys_menu VALUES (501, N'登录日志', 108, 2, N'logininfor', N'monitor/logininfor/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:logininfor:list', N'logininfor', N'admin', getdate(), N'', NULL, N'登录日志菜单') GO INSERT sys_menu VALUES (1001, N'用户查询', 100, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1002, N'用户新增', 100, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1003, N'用户修改', 100, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1004, N'用户删除', 100, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1005, N'用户导出', 100, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:export', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1006, N'用户导入', 100, 6, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:import', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1007, N'重置密码', 100, 7, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:resetPwd', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1008, N'角色查询', 101, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1009, N'角色新增', 101, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1010, N'角色修改', 101, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1011, N'角色删除', 101, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1012, N'角色导出', 101, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:export', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1013, N'菜单查询', 102, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1014, N'菜单新增', 102, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1015, N'菜单修改', 102, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1016, N'菜单删除', 102, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1017, N'部门查询', 103, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1018, N'部门新增', 103, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1019, N'部门修改', 103, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1020, N'部门删除', 103, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1021, N'岗位查询', 104, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1022, N'岗位新增', 104, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1023, N'岗位修改', 104, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1024, N'岗位删除', 104, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1025, N'岗位导出', 104, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:export', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1026, N'字典查询', 105, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1027, N'字典新增', 105, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1028, N'字典修改', 105, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1029, N'字典删除', 105, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1030, N'字典导出', 105, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:export', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1031, N'参数查询', 106, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1032, N'参数新增', 106, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1033, N'参数修改', 106, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1034, N'参数删除', 106, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1035, N'参数导出', 106, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:export', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1036, N'公告查询', 107, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1037, N'公告新增', 107, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:add', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1038, N'公告修改', 107, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1039, N'公告删除', 107, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1040, N'操作查询', 500, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1041, N'操作删除', 500, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1042, N'日志导出', 500, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:export', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1043, N'登录查询', 501, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1044, N'登录删除', 501, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1045, N'日志导出', 501, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:export', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', N'admin', getdate(), N'', null, N'') GO INSERT sys_menu VALUES (1046, N'在线查询', 109, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1047, N'批量强退', 109, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:batchLogout', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1048, N'单条强退', 109, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:forceLogout', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1055, N'生成查询', 115, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:query', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1056, N'生成修改', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:edit', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1057, N'生成删除', 115, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:remove', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1058, N'导入代码', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:import', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1059, N'预览代码', 115, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:preview', N'#', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_menu VALUES (1060, N'生成代码', 115, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:code', N'#', N'admin', getdate(), N'', NULL, N'') GO -- oss相关按钮 INSERT sys_menu VALUES (1600, N'文件查询', 118, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:query', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1601, N'文件上传', 118, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:upload', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1602, N'文件下载', 118, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:download', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1603, N'文件删除', 118, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:remove', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1604, N'配置添加', 118, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:add', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1605, N'配置编辑', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:edit', N'#', N'admin', getdate(), N'', NULL, N''); GO CREATE TABLE sys_notice ( notice_id bigint NOT NULL, notice_title nvarchar(50) NOT NULL, notice_type nchar(1) NOT NULL, notice_content nvarchar(max) NULL, status nchar(1) DEFAULT ('0') NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(255) NULL, CONSTRAINT PK__sys_noti__3E82A5DB0EC94801 PRIMARY KEY CLUSTERED (notice_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'公告ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'notice_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'公告标题' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'notice_title' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'公告类型(1通知 2公告)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'notice_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'公告内容' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'notice_content' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'公告状态(0正常 1关闭)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'通知公告表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_notice' GO INSERT sys_notice VALUES (1, N'温馨提醒:2018-07-01 若依新版本发布啦', N'2', N'新版本内容', N'0', N'admin', getdate(), N'admin', getdate(), N'管理员') GO INSERT sys_notice VALUES (2, N'维护通知:2018-07-01 若依系统凌晨维护', N'1', N'维护内容', N'0', N'admin', getdate(), N'admin', getdate(), N'管理员') GO CREATE TABLE sys_oper_log ( oper_id bigint NOT NULL, title nvarchar(50) DEFAULT '' NULL, business_type int DEFAULT ((0)) NULL, method nvarchar(100) DEFAULT '' NULL, request_method nvarchar(10) DEFAULT '' NULL, operator_type int DEFAULT ((0)) NULL, oper_name nvarchar(50) DEFAULT '' NULL, dept_name nvarchar(50) DEFAULT '' NULL, oper_url nvarchar(255) DEFAULT '' NULL, oper_ip nvarchar(128) DEFAULT '' NULL, oper_location nvarchar(255) DEFAULT '' NULL, oper_param nvarchar(2000) DEFAULT '' NULL, json_result nvarchar(2000) DEFAULT '' NULL, status int DEFAULT ((0)) NULL, error_msg nvarchar(2000) DEFAULT '' NULL, oper_time datetime2(7) NULL, CONSTRAINT PK__sys_oper__34723BF9BD954573 PRIMARY KEY CLUSTERED (oper_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO CREATE NONCLUSTERED INDEX idx_sys_oper_log_bt ON sys_oper_log (business_type) GO CREATE NONCLUSTERED INDEX idx_sys_oper_log_s ON sys_oper_log (status) GO CREATE NONCLUSTERED INDEX idx_sys_oper_log_ot ON sys_oper_log (oper_time) GO EXEC sys.sp_addextendedproperty 'MS_Description', N'日志主键' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'oper_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'模块标题' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'title' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'业务类型(0其它 1新增 2修改 3删除)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'business_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'方法名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'method' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'请求方式' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'request_method' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'操作类别(0其它 1后台用户 2手机端用户)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'operator_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'操作人员' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'oper_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'dept_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'请求URL' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'oper_url' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'主机地址' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'oper_ip' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'操作地点' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'oper_location' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'请求参数' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'oper_param' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'返回参数' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'json_result' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'操作状态(0正常 1异常)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'错误消息' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'error_msg' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'操作时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log', 'COLUMN', N'oper_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'操作日志记录' , 'SCHEMA', N'dbo', 'TABLE', N'sys_oper_log' GO CREATE TABLE sys_post ( post_id bigint NOT NULL, post_code nvarchar(64) NOT NULL, post_name nvarchar(50) NOT NULL, post_sort int NOT NULL, status nchar(1) NOT NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__sys_post__3ED7876668E2D081 PRIMARY KEY CLUSTERED (post_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'岗位ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'post_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'岗位编码' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'post_code' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'岗位名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'post_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'显示顺序' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'post_sort' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'状态(0正常 1停用)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'岗位信息表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_post' GO INSERT sys_post VALUES (1, N'ceo', N'董事长', 1, N'0', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_post VALUES (2, N'se', N'项目经理', 2, N'0', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_post VALUES (3, N'hr', N'人力资源', 3, N'0', N'admin', getdate(), N'', NULL, N'') GO INSERT sys_post VALUES (4, N'user', N'普通员工', 4, N'0', N'admin', getdate(), N'', NULL, N'') GO CREATE TABLE sys_role ( role_id bigint NOT NULL, role_name nvarchar(30) NOT NULL, role_key nvarchar(100) NOT NULL, role_sort int NOT NULL, data_scope nchar(1) DEFAULT ('1') NULL, menu_check_strictly tinyint DEFAULT ((1)) NULL, dept_check_strictly tinyint DEFAULT ((1)) NULL, status nchar(1) NOT NULL, del_flag nchar(1) DEFAULT ('0') NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__sys_role__760965CCF9383145 PRIMARY KEY CLUSTERED (role_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'role_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色名称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'role_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色权限字符串' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'role_key' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'显示顺序' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'role_sort' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'data_scope' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单树选择项是否关联显示' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'menu_check_strictly' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门树选择项是否关联显示' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'dept_check_strictly' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色状态(0正常 1停用)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'删除标志(0代表存在 2代表删除)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'del_flag' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色信息表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role' GO INSERT sys_role VALUES (1, N'超级管理员', N'admin', 1, N'1', 1, 1, N'0', N'0', N'admin', getdate(), N'', NULL, N'超级管理员') GO INSERT sys_role VALUES (2, N'普通角色', N'common', 2, N'2', 1, 1, N'0', N'0', N'admin', getdate(), N'', NULL, N'普通角色') GO CREATE TABLE sys_role_dept ( role_id bigint NOT NULL, dept_id bigint NOT NULL, CONSTRAINT PK__sys_role__2BC3005BABBCA08A PRIMARY KEY CLUSTERED (role_id, dept_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role_dept', 'COLUMN', N'role_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role_dept', 'COLUMN', N'dept_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色和部门关联表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role_dept' GO INSERT sys_role_dept VALUES (2, 100) GO INSERT sys_role_dept VALUES (2, 101) GO INSERT sys_role_dept VALUES (2, 105) GO CREATE TABLE sys_role_menu ( role_id bigint NOT NULL, menu_id bigint NOT NULL, CONSTRAINT PK__sys_role__A2C36A6187BA4B17 PRIMARY KEY CLUSTERED (role_id, menu_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role_menu', 'COLUMN', N'role_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'菜单ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role_menu', 'COLUMN', N'menu_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色和菜单关联表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_role_menu' GO INSERT sys_role_menu VALUES (2, 1) GO INSERT sys_role_menu VALUES (2, 2) GO INSERT sys_role_menu VALUES (2, 3) GO INSERT sys_role_menu VALUES (2, 100) GO INSERT sys_role_menu VALUES (2, 101) GO INSERT sys_role_menu VALUES (2, 102) GO INSERT sys_role_menu VALUES (2, 103) GO INSERT sys_role_menu VALUES (2, 104) GO INSERT sys_role_menu VALUES (2, 105) GO INSERT sys_role_menu VALUES (2, 106) GO INSERT sys_role_menu VALUES (2, 107) GO INSERT sys_role_menu VALUES (2, 108) GO INSERT sys_role_menu VALUES (2, 109) GO INSERT sys_role_menu VALUES (2, 110) GO INSERT sys_role_menu VALUES (2, 111) GO INSERT sys_role_menu VALUES (2, 112) GO INSERT sys_role_menu VALUES (2, 113) GO INSERT sys_role_menu VALUES (2, 114) GO INSERT sys_role_menu VALUES (2, 115) GO INSERT sys_role_menu VALUES (2, 116) GO INSERT sys_role_menu VALUES (2, 500) GO INSERT sys_role_menu VALUES (2, 501) GO INSERT sys_role_menu VALUES (2, 1001) GO INSERT sys_role_menu VALUES (2, 1002) GO INSERT sys_role_menu VALUES (2, 1003) GO INSERT sys_role_menu VALUES (2, 1004) GO INSERT sys_role_menu VALUES (2, 1005) GO INSERT sys_role_menu VALUES (2, 1006) GO INSERT sys_role_menu VALUES (2, 1007) GO INSERT sys_role_menu VALUES (2, 1008) GO INSERT sys_role_menu VALUES (2, 1009) GO INSERT sys_role_menu VALUES (2, 1010) GO INSERT sys_role_menu VALUES (2, 1011) GO INSERT sys_role_menu VALUES (2, 1012) GO INSERT sys_role_menu VALUES (2, 1013) GO INSERT sys_role_menu VALUES (2, 1014) GO INSERT sys_role_menu VALUES (2, 1015) GO INSERT sys_role_menu VALUES (2, 1016) GO INSERT sys_role_menu VALUES (2, 1017) GO INSERT sys_role_menu VALUES (2, 1018) GO INSERT sys_role_menu VALUES (2, 1019) GO INSERT sys_role_menu VALUES (2, 1020) GO INSERT sys_role_menu VALUES (2, 1021) GO INSERT sys_role_menu VALUES (2, 1022) GO INSERT sys_role_menu VALUES (2, 1023) GO INSERT sys_role_menu VALUES (2, 1024) GO INSERT sys_role_menu VALUES (2, 1025) GO INSERT sys_role_menu VALUES (2, 1026) GO INSERT sys_role_menu VALUES (2, 1027) GO INSERT sys_role_menu VALUES (2, 1028) GO INSERT sys_role_menu VALUES (2, 1029) GO INSERT sys_role_menu VALUES (2, 1030) GO INSERT sys_role_menu VALUES (2, 1031) GO INSERT sys_role_menu VALUES (2, 1032) GO INSERT sys_role_menu VALUES (2, 1033) GO INSERT sys_role_menu VALUES (2, 1034) GO INSERT sys_role_menu VALUES (2, 1035) GO INSERT sys_role_menu VALUES (2, 1036) GO INSERT sys_role_menu VALUES (2, 1037) GO INSERT sys_role_menu VALUES (2, 1038) GO INSERT sys_role_menu VALUES (2, 1039) GO INSERT sys_role_menu VALUES (2, 1040) GO INSERT sys_role_menu VALUES (2, 1041) GO INSERT sys_role_menu VALUES (2, 1042) GO INSERT sys_role_menu VALUES (2, 1043) GO INSERT sys_role_menu VALUES (2, 1044) GO INSERT sys_role_menu VALUES (2, 1045) GO INSERT sys_role_menu VALUES (2, 1046) GO INSERT sys_role_menu VALUES (2, 1047) GO INSERT sys_role_menu VALUES (2, 1048) GO INSERT sys_role_menu VALUES (2, 1049) GO INSERT sys_role_menu VALUES (2, 1050) GO INSERT sys_role_menu VALUES (2, 1051) GO INSERT sys_role_menu VALUES (2, 1052) GO INSERT sys_role_menu VALUES (2, 1053) GO INSERT sys_role_menu VALUES (2, 1054) GO INSERT sys_role_menu VALUES (2, 1055) GO INSERT sys_role_menu VALUES (2, 1056) GO INSERT sys_role_menu VALUES (2, 1057) GO INSERT sys_role_menu VALUES (2, 1058) GO INSERT sys_role_menu VALUES (2, 1059) GO INSERT sys_role_menu VALUES (2, 1060) GO CREATE TABLE sys_user ( user_id bigint NOT NULL, dept_id bigint NULL, user_name nvarchar(30) NOT NULL, nick_name nvarchar(30) NOT NULL, user_type nvarchar(10) DEFAULT ('sys_user') NULL, email nvarchar(50) DEFAULT '' NULL, phonenumber nvarchar(11) DEFAULT '' NULL, sex nchar(1) DEFAULT ('0') NULL, avatar nvarchar(100) DEFAULT '' NULL, password nvarchar(100) DEFAULT '' NULL, status nchar(1) DEFAULT ('0') NULL, del_flag nchar(1) DEFAULT ('0') NULL, login_ip nvarchar(128) DEFAULT '' NULL, login_date datetime2(7) NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__sys_user__B9BE370F79170B6A PRIMARY KEY CLUSTERED (user_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'user_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'部门ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'dept_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户账号' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'user_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户昵称' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'nick_name' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户类型(sys_user系统用户)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'user_type' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户邮箱' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'email' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'手机号码' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'phonenumber' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户性别(0男 1女 2未知)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'sex' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'头像地址' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'avatar' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'密码' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'password' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'帐号状态(0正常 1停用)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'status' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'删除标志(0代表存在 2代表删除)' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'del_flag' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'最后登录IP' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'login_ip' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'最后登录时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'login_date' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'create_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'创建时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'create_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新者' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'update_by' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'更新时间' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'update_time' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'备注' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user', 'COLUMN', N'remark' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户信息表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user' GO INSERT sys_user VALUES (1, 103, N'admin', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@163.com', N'15888888888', N'1', N'', N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'', getdate(), N'管理员') GO INSERT sys_user VALUES (2, 105, N'lionli', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@qq.com', N'15666666666', N'1', N'', N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'admin', getdate(), N'测试员') GO CREATE TABLE sys_user_post ( user_id bigint NOT NULL, post_id bigint NOT NULL, CONSTRAINT PK__sys_user__CA534F799C04589B PRIMARY KEY CLUSTERED (user_id, post_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user_post', 'COLUMN', N'user_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'岗位ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user_post', 'COLUMN', N'post_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户与岗位关联表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user_post' GO INSERT sys_user_post VALUES (1, 1) GO INSERT sys_user_post VALUES (2, 2) GO CREATE TABLE sys_user_role ( user_id bigint NOT NULL, role_id bigint NOT NULL, CONSTRAINT PK__sys_user__6EDEA153FB34D8F0 PRIMARY KEY CLUSTERED (user_id, role_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user_role', 'COLUMN', N'user_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'角色ID' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user_role', 'COLUMN', N'role_id' GO EXEC sys.sp_addextendedproperty 'MS_Description', N'用户和角色关联表' , 'SCHEMA', N'dbo', 'TABLE', N'sys_user_role' GO INSERT sys_user_role VALUES (1, 1) GO INSERT sys_user_role VALUES (2, 2) GO CREATE TABLE sys_oss ( oss_id bigint NOT NULL, file_name nvarchar(255) DEFAULT '' NOT NULL, original_name nvarchar(255) DEFAULT '' NOT NULL, file_suffix nvarchar(10) DEFAULT '' NOT NULL, url nvarchar(500) NOT NULL, create_time datetime2(7) NULL, create_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, service nvarchar(20) DEFAULT ('minio') NOT NULL, CONSTRAINT PK__sys_oss__91241EA442389F0D PRIMARY KEY CLUSTERED (oss_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sp_addextendedproperty 'MS_Description', N'对象存储主键', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'oss_id' GO EXEC sp_addextendedproperty 'MS_Description', N'文件名', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'file_name' GO EXEC sp_addextendedproperty 'MS_Description', N'原名', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'original_name' GO EXEC sp_addextendedproperty 'MS_Description', N'文件后缀名', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'file_suffix' GO EXEC sp_addextendedproperty 'MS_Description', N'URL地址', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'url' GO EXEC sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'create_time' GO EXEC sp_addextendedproperty 'MS_Description', N'上传人', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'create_by' GO EXEC sp_addextendedproperty 'MS_Description', N'更新时间', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'update_time' GO EXEC sp_addextendedproperty 'MS_Description', N'更新人', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'update_by' GO EXEC sp_addextendedproperty 'MS_Description', N'服务商', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss', 'COLUMN', N'service' GO EXEC sp_addextendedproperty 'MS_Description', N'OSS对象存储表', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss' GO CREATE TABLE sys_oss_config ( oss_config_id bigint NOT NULL, config_key nvarchar(20) DEFAULT '' NOT NULL, access_key nvarchar(255) DEFAULT '' NULL, secret_key nvarchar(255) DEFAULT '' NULL, bucket_name nvarchar(255) DEFAULT '' NULL, prefix nvarchar(255) DEFAULT '' NULL, endpoint nvarchar(255) DEFAULT '' NULL, domain nvarchar(255) DEFAULT '' NULL, is_https nchar(1) DEFAULT ('N') NULL, region nvarchar(255) DEFAULT '' NULL, access_policy nchar(1) DEFAULT ('1') NOT NULL, status nchar(1) DEFAULT ('1') NULL, ext1 nvarchar(255) DEFAULT '' NULL, create_by nvarchar(64) DEFAULT '' NULL, create_time datetime2(7) NULL, update_by nvarchar(64) DEFAULT '' NULL, update_time datetime2(7) NULL, remark nvarchar(500) NULL, CONSTRAINT PK__sys_oss___BFBDE87009ED2882 PRIMARY KEY CLUSTERED (oss_config_id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sp_addextendedproperty 'MS_Description', N'主建', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'oss_config_id' GO EXEC sp_addextendedproperty 'MS_Description', N'配置key', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'config_key' GO EXEC sp_addextendedproperty 'MS_Description', N'accessKey', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'access_key' GO EXEC sp_addextendedproperty 'MS_Description', N'秘钥', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'secret_key' GO EXEC sp_addextendedproperty 'MS_Description', N'桶名称', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'bucket_name' GO EXEC sp_addextendedproperty 'MS_Description', N'前缀', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'prefix' GO EXEC sp_addextendedproperty 'MS_Description', N'访问站点', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'endpoint' GO EXEC sp_addextendedproperty 'MS_Description', N'自定义域名', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'domain' GO EXEC sp_addextendedproperty 'MS_Description', N'是否https(Y=是,N=否)', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'is_https' GO EXEC sp_addextendedproperty 'MS_Description', N'域', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'region' GO EXEC sp_addextendedproperty 'MS_Description', N'桶权限类型(0=private 1=public 2=custom)', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'access_policy' GO EXEC sp_addextendedproperty 'MS_Description', N'是否默认(0=是,1=否)', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'status' GO EXEC sp_addextendedproperty 'MS_Description', N'扩展字段', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'ext1' GO EXEC sp_addextendedproperty 'MS_Description', N'创建者', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'create_by' GO EXEC sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'create_time' GO EXEC sp_addextendedproperty 'MS_Description', N'更新者', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'update_by' GO EXEC sp_addextendedproperty 'MS_Description', N'更新时间', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'update_time' GO EXEC sp_addextendedproperty 'MS_Description', N'备注', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'remark' GO EXEC sp_addextendedproperty 'MS_Description', N'对象存储配置表', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config' GO INSERT INTO sys_oss_config VALUES (N'1', N'minio', N'ruoyi', N'ruoyi123', N'ruoyi', N'', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'0', N'', N'admin', getdate(), N'admin', getdate(), NULL) GO INSERT INTO sys_oss_config VALUES (N'2', N'qiniu', N'XXXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N's3-cn-north-1.qiniucs.com', N'',N'N', N'', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) GO INSERT INTO sys_oss_config VALUES (N'3', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) GO INSERT INTO sys_oss_config VALUES (N'4', N'qcloud', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'ruoyi-1250000000', N'', N'cos.ap-beijing.myqcloud.com', N'',N'N', N'ap-beijing', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) GO INSERT INTO sys_oss_config VALUES (N'5', N'image', N'ruoyi', N'ruoyi123', N'ruoyi', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) GO ================================================ FILE: script/sql/sqlserver/sqlserver_test.sql ================================================ CREATE TABLE test_demo ( id bigint NOT NULL, dept_id bigint NULL, user_id bigint NULL, order_num int DEFAULT ((0)) NULL, test_key nvarchar(255) NULL, value nvarchar(255) NULL, version int DEFAULT ((0)) NULL, create_time datetime2(0) NULL, create_by nvarchar(64) NULL, update_time datetime2(0) NULL, update_by nvarchar(64) NULL, del_flag int DEFAULT ((0)) NULL, CONSTRAINT PK__test_dem__3213E83F176051C8 PRIMARY KEY CLUSTERED (id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sp_addextendedproperty 'MS_Description', N'主键', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'id' GO EXEC sp_addextendedproperty 'MS_Description', N'部门id', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'dept_id' GO EXEC sp_addextendedproperty 'MS_Description', N'用户id', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'user_id' GO EXEC sp_addextendedproperty 'MS_Description', N'排序号', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'order_num' GO EXEC sp_addextendedproperty 'MS_Description', N'key键', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'test_key' GO EXEC sp_addextendedproperty 'MS_Description', N'值', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'value' GO EXEC sp_addextendedproperty 'MS_Description', N'版本', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'version' GO EXEC sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'create_time' GO EXEC sp_addextendedproperty 'MS_Description', N'创建人', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'create_by' GO EXEC sp_addextendedproperty 'MS_Description', N'更新时间', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'update_time' GO EXEC sp_addextendedproperty 'MS_Description', N'更新人', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'update_by' GO EXEC sp_addextendedproperty 'MS_Description', N'删除标志', 'SCHEMA', N'dbo', 'TABLE', N'test_demo', 'COLUMN', N'del_flag' GO EXEC sp_addextendedproperty 'MS_Description', N'测试单表', 'SCHEMA', N'dbo', 'TABLE', N'test_demo' GO CREATE TABLE test_tree ( id bigint NOT NULL, parent_id bigint DEFAULT ((0)) NULL, dept_id bigint NULL, user_id bigint NULL, tree_name nvarchar(255) NULL, version int DEFAULT ((0)) NULL, create_time datetime2(0) NULL, create_by nvarchar(64) NULL, update_time datetime2(0) NULL, update_by nvarchar(64) NULL, del_flag int DEFAULT ((0)) NULL, CONSTRAINT PK__test_tre__3213E83FC75A1B63 PRIMARY KEY CLUSTERED (id) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO EXEC sp_addextendedproperty 'MS_Description', N'主键', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'id' GO EXEC sp_addextendedproperty 'MS_Description', N'父id', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'parent_id' GO EXEC sp_addextendedproperty 'MS_Description', N'部门id', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'dept_id' GO EXEC sp_addextendedproperty 'MS_Description', N'用户id', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'user_id' GO EXEC sp_addextendedproperty 'MS_Description', N'值', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'tree_name' GO EXEC sp_addextendedproperty 'MS_Description', N'版本', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'version' GO EXEC sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'create_time' GO EXEC sp_addextendedproperty 'MS_Description', N'创建人', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'create_by' GO EXEC sp_addextendedproperty 'MS_Description', N'更新时间', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'update_time' GO EXEC sp_addextendedproperty 'MS_Description', N'更新人', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'update_by' GO EXEC sp_addextendedproperty 'MS_Description', N'删除标志', 'SCHEMA', N'dbo', 'TABLE', N'test_tree', 'COLUMN', N'del_flag' GO EXEC sp_addextendedproperty 'MS_Description', N'测试树表', 'SCHEMA', N'dbo', 'TABLE', N'test_tree' GO INSERT sys_user VALUES (3, 108, N'test', N'本部门及以下 密码666666', N'sys_user', N'', N'', N'0', N'', N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'test', getdate(), NULL); GO INSERT sys_user VALUES (4, 102, N'test1', N'仅本人 密码666666', N'sys_user', N'', N'', N'0', N'', N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), N'admin', getdate(), N'test1', getdate(), NULL); GO INSERT sys_menu VALUES (5, N'测试菜单', 0, 5, N'demo', NULL, 1, 0, N'M', N'0', N'0', NULL, N'star', N'admin', getdate(), NULL, NULL, N''); GO INSERT sys_menu VALUES (1500, N'测试单表', 5, 1, N'demo', N'demo/demo/index', 1, 0, N'C', N'0', N'0', N'demo:demo:list', N'#', N'admin', getdate(), N'', NULL, N'测试单表菜单'); GO INSERT sys_menu VALUES (1501, N'测试单表查询', 1500, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:query', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1502, N'测试单表新增', 1500, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:add', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1503, N'测试单表修改', 1500, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:edit', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1504, N'测试单表删除', 1500, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:remove', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1505, N'测试单表导出', 1500, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:export', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1506, N'测试树表', 5, 1, N'tree', N'demo/tree/index', 1, 0, N'C', N'0', N'0', N'demo:tree:list', N'#', N'admin', getdate(), N'', NULL, N'测试树表菜单'); GO INSERT sys_menu VALUES (1507, N'测试树表查询', 1506, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:query', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1508, N'测试树表新增', 1506, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:add', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1509, N'测试树表修改', 1506, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:edit', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1510, N'测试树表删除', 1506, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:remove', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_menu VALUES (1511, N'测试树表导出', 1506, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:export', N'#', N'admin', getdate(), N'', NULL, N''); GO INSERT sys_role VALUES (3, N'本部门及以下', N'test1', 3, N'4', 1, 1, N'0', N'0', N'admin', getdate(), N'admin', NULL, NULL); GO INSERT sys_role VALUES (4, N'仅本人', N'test2', 4, N'5', 1, 1, N'0', N'0', N'admin', getdate(), N'admin', NULL, NULL); GO INSERT sys_role_menu VALUES (3, 1); GO INSERT sys_role_menu VALUES (3, 5); GO INSERT sys_role_menu VALUES (3, 100); GO INSERT sys_role_menu VALUES (3, 101); GO INSERT sys_role_menu VALUES (3, 102); GO INSERT sys_role_menu VALUES (3, 103); GO INSERT sys_role_menu VALUES (3, 104); GO INSERT sys_role_menu VALUES (3, 105); GO INSERT sys_role_menu VALUES (3, 106); GO INSERT sys_role_menu VALUES (3, 107); GO INSERT sys_role_menu VALUES (3, 108); GO INSERT sys_role_menu VALUES (3, 500); GO INSERT sys_role_menu VALUES (3, 501); GO INSERT sys_role_menu VALUES (3, 1001); GO INSERT sys_role_menu VALUES (3, 1002); GO INSERT sys_role_menu VALUES (3, 1003); GO INSERT sys_role_menu VALUES (3, 1004); GO INSERT sys_role_menu VALUES (3, 1005); GO INSERT sys_role_menu VALUES (3, 1006); GO INSERT sys_role_menu VALUES (3, 1007); GO INSERT sys_role_menu VALUES (3, 1008); GO INSERT sys_role_menu VALUES (3, 1009); GO INSERT sys_role_menu VALUES (3, 1010); GO INSERT sys_role_menu VALUES (3, 1011); GO INSERT sys_role_menu VALUES (3, 1012); GO INSERT sys_role_menu VALUES (3, 1013); GO INSERT sys_role_menu VALUES (3, 1014); GO INSERT sys_role_menu VALUES (3, 1015); GO INSERT sys_role_menu VALUES (3, 1016); GO INSERT sys_role_menu VALUES (3, 1017); GO INSERT sys_role_menu VALUES (3, 1018); GO INSERT sys_role_menu VALUES (3, 1019); GO INSERT sys_role_menu VALUES (3, 1020); GO INSERT sys_role_menu VALUES (3, 1021); GO INSERT sys_role_menu VALUES (3, 1022); GO INSERT sys_role_menu VALUES (3, 1023); GO INSERT sys_role_menu VALUES (3, 1024); GO INSERT sys_role_menu VALUES (3, 1025); GO INSERT sys_role_menu VALUES (3, 1026); GO INSERT sys_role_menu VALUES (3, 1027); GO INSERT sys_role_menu VALUES (3, 1028); GO INSERT sys_role_menu VALUES (3, 1029); GO INSERT sys_role_menu VALUES (3, 1030); GO INSERT sys_role_menu VALUES (3, 1031); GO INSERT sys_role_menu VALUES (3, 1032); GO INSERT sys_role_menu VALUES (3, 1033); GO INSERT sys_role_menu VALUES (3, 1034); GO INSERT sys_role_menu VALUES (3, 1035); GO INSERT sys_role_menu VALUES (3, 1036); GO INSERT sys_role_menu VALUES (3, 1037); GO INSERT sys_role_menu VALUES (3, 1038); GO INSERT sys_role_menu VALUES (3, 1039); GO INSERT sys_role_menu VALUES (3, 1040); GO INSERT sys_role_menu VALUES (3, 1041); GO INSERT sys_role_menu VALUES (3, 1042); GO INSERT sys_role_menu VALUES (3, 1043); GO INSERT sys_role_menu VALUES (3, 1044); GO INSERT sys_role_menu VALUES (3, 1045); GO INSERT sys_role_menu VALUES (3, 1500); GO INSERT sys_role_menu VALUES (3, 1501); GO INSERT sys_role_menu VALUES (3, 1502); GO INSERT sys_role_menu VALUES (3, 1503); GO INSERT sys_role_menu VALUES (3, 1504); GO INSERT sys_role_menu VALUES (3, 1505); GO INSERT sys_role_menu VALUES (3, 1506); GO INSERT sys_role_menu VALUES (3, 1507); GO INSERT sys_role_menu VALUES (3, 1508); GO INSERT sys_role_menu VALUES (3, 1509); GO INSERT sys_role_menu VALUES (3, 1510); GO INSERT sys_role_menu VALUES (3, 1511); GO INSERT sys_role_menu VALUES (4, 5); GO INSERT sys_role_menu VALUES (4, 1500); GO INSERT sys_role_menu VALUES (4, 1501); GO INSERT sys_role_menu VALUES (4, 1502); GO INSERT sys_role_menu VALUES (4, 1503); GO INSERT sys_role_menu VALUES (4, 1504); GO INSERT sys_role_menu VALUES (4, 1505); GO INSERT sys_role_menu VALUES (4, 1506); GO INSERT sys_role_menu VALUES (4, 1507); GO INSERT sys_role_menu VALUES (4, 1508); GO INSERT sys_role_menu VALUES (4, 1509); GO INSERT sys_role_menu VALUES (4, 1510); GO INSERT sys_role_menu VALUES (4, 1511); GO INSERT sys_user_role VALUES (3, 3); GO INSERT sys_user_role VALUES (4, 4); GO INSERT test_demo VALUES (1, 102, 4, 1, N'测试数据权限', N'测试', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (2, 102, 3, 2, N'子节点1', N'111', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (3, 102, 3, 3, N'子节点2', N'222', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (4, 108, 4, 4, N'测试数据', N'demo', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (5, 108, 3, 13, N'子节点11', N'1111', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (6, 108, 3, 12, N'子节点22', N'2222', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (7, 108, 3, 11, N'子节点33', N'3333', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (8, 108, 3, 10, N'子节点44', N'4444', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (9, 108, 3, 9, N'子节点55', N'5555', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (10, 108, 3, 8, N'子节点66', N'6666', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (11, 108, 3, 7, N'子节点77', N'7777', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (12, 108, 3, 6, N'子节点88', N'8888', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_demo VALUES (13, 108, 3, 5, N'子节点99', N'9999', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (1, 0, 102, 4, N'测试数据权限', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (2, 1, 102, 3, N'子节点1', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (3, 2, 102, 3, N'子节点2', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (4, 0, 108, 4, N'测试树1', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (5, 4, 108, 3, N'子节点11', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (6, 4, 108, 3, N'子节点22', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (7, 4, 108, 3, N'子节点33', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (8, 5, 108, 3, N'子节点44', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (9, 6, 108, 3, N'子节点55', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (10, 7, 108, 3, N'子节点66', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (11, 7, 108, 3, N'子节点77', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (12, 10, 108, 3, N'子节点88', 0, getdate(), N'admin', NULL, NULL, 0); GO INSERT test_tree VALUES (13, 10, 108, 3, N'子节点99', 0, getdate(), N'admin', NULL, NULL, 0); GO ================================================ FILE: script/sql/tables_xxl_job.sql ================================================ # # XXL-JOB v2.3.0 # Copyright (c) 2015-present, xuxueli. SET NAMES utf8mb4; CREATE TABLE `xxl_job_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `job_group` int(11) NOT NULL COMMENT '执行器主键ID', `job_desc` varchar(255) NOT NULL, `add_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, `author` varchar(64) DEFAULT NULL COMMENT '作者', `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件', `schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型', `schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置,值含义取决于调度类型', `misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略', `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略', `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', `executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略', `executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒', `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型', `glue_source` mediumtext COMMENT 'GLUE源代码', `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间', `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔', `trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行', `trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间', `trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `job_group` int(11) NOT NULL COMMENT '执行器主键ID', `job_id` int(11) NOT NULL COMMENT '任务,主键ID', `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址', `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', `executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2', `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', `trigger_code` int(11) NOT NULL COMMENT '调度-结果', `trigger_msg` text COMMENT '调度-日志', `handle_time` datetime DEFAULT NULL COMMENT '执行-时间', `handle_code` int(11) NOT NULL COMMENT '执行-状态', `handle_msg` text COMMENT '执行-日志', `alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败', PRIMARY KEY (`id`), KEY `I_trigger_time` (`trigger_time`), KEY `I_handle_code` (`handle_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_log_report` ( `id` int(11) NOT NULL AUTO_INCREMENT, `trigger_day` datetime DEFAULT NULL COMMENT '调度-时间', `running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量', `suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量', `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量', `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_logglue` ( `id` int(11) NOT NULL AUTO_INCREMENT, `job_id` int(11) NOT NULL COMMENT '任务,主键ID', `glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型', `glue_source` mediumtext COMMENT 'GLUE源代码', `glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注', `add_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_registry` ( `id` int(11) NOT NULL AUTO_INCREMENT, `registry_group` varchar(50) NOT NULL, `registry_key` varchar(255) NOT NULL, `registry_value` varchar(255) NOT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_group` ( `id` int(11) NOT NULL AUTO_INCREMENT, `app_name` varchar(64) NOT NULL COMMENT '执行器AppName', `title` varchar(12) NOT NULL COMMENT '执行器名称', `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入', `address_list` text COMMENT '执行器地址列表,多地址逗号分隔', `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '账号', `password` varchar(50) NOT NULL COMMENT '密码', `role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员', `permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割', PRIMARY KEY (`id`), UNIQUE KEY `i_username` (`username`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `xxl_job_lock` ( `lock_name` varchar(50) NOT NULL COMMENT '锁名称', PRIMARY KEY (`lock_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`, `update_time`) VALUES (1, 'xxl-job-executor', '示例执行器', 0, NULL, '2018-11-03 22:21:31' ); INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', ''); INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL); INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock'); commit; ================================================ FILE: script/sql/test.sql ================================================ DROP TABLE if EXISTS test_demo; CREATE TABLE test_demo ( id bigint(0) NOT NULL COMMENT '主键', dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id', user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id', order_num int(0) NULL DEFAULT 0 COMMENT '排序号', test_key varchar(255) NULL DEFAULT NULL COMMENT 'key键', value varchar(255) NULL DEFAULT NULL COMMENT '值', version int(0) NULL DEFAULT 0 COMMENT '版本', create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', create_by varchar(64) NULL DEFAULT NULL COMMENT '创建人', update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', update_by varchar(64) NULL DEFAULT NULL COMMENT '更新人', del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', PRIMARY KEY (id) USING BTREE ) ENGINE = InnoDB COMMENT = '测试单表'; DROP TABLE if EXISTS test_tree; CREATE TABLE test_tree ( id bigint(0) NOT NULL COMMENT '主键', parent_id bigint(0) NULL DEFAULT 0 COMMENT '父id', dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id', user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id', tree_name varchar(255) NULL DEFAULT NULL COMMENT '值', version int(0) NULL DEFAULT 0 COMMENT '版本', create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', create_by varchar(64) NULL DEFAULT NULL COMMENT '创建人', update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', update_by varchar(64) NULL DEFAULT NULL COMMENT '更新人', del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', PRIMARY KEY (id) USING BTREE ) ENGINE = InnoDB COMMENT = '测试树表'; INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (3, 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), 'test', sysdate(), NULL); INSERT INTO sys_user(user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark) VALUES (4, 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', '', '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), 'test1', sysdate(), NULL); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (5, '测试菜单', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 'admin', sysdate(), NULL, NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1500, '测试单表', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 'admin', sysdate(), '', NULL, '测试单表菜单'); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1501, '测试单表查询', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1502, '测试单表新增', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1503, '测试单表修改', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1504, '测试单表删除', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1505, '测试单表导出', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1506, '测试树表', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 'admin', sysdate(), '', NULL, '测试树表菜单'); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1507, '测试树表查询', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1508, '测试树表新增', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1509, '测试树表修改', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1510, '测试树表删除', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES (1511, '测试树表导出', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 'admin', sysdate(), '', NULL, ''); INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (3, '本部门及以下', 'test1', 3, '4', 1, 1, '0', '0', 'admin', sysdate(), 'admin', NULL, NULL); INSERT INTO sys_role(role_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES (4, '仅本人', 'test2', 4, '5', 1, 1, '0', '0', 'admin', sysdate(), 'admin', NULL, NULL); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510); INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511); INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3); INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 102, 4, 1, '测试数据权限', '测试', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 102, 3, 2, '子节点1', '111', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 102, 3, 3, '子节点2', '222', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 108, 4, 4, '测试数据', 'demo', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 108, 3, 13, '子节点11', '1111', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 108, 3, 12, '子节点22', '2222', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 108, 3, 11, '子节点33', '3333', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 108, 3, 10, '子节点44', '4444', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 108, 3, 9, '子节点55', '5555', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 108, 3, 8, '子节点66', '6666', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 108, 3, 7, '子节点77', '7777', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 108, 3, 6, '子节点88', '8888', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_demo(id, dept_id, user_id, order_num, test_key, value, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 108, 3, 5, '子节点99', '9999', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (1, 0, 102, 4, '测试数据权限', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (2, 1, 102, 3, '子节点1', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (3, 2, 102, 3, '子节点2', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (4, 0, 108, 4, '测试树1', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (5, 4, 108, 3, '子节点11', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (6, 4, 108, 3, '子节点22', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (7, 4, 108, 3, '子节点33', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (8, 5, 108, 3, '子节点44', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (9, 6, 108, 3, '子节点55', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (10, 7, 108, 3, '子节点66', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (11, 7, 108, 3, '子节点77', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (12, 10, 108, 3, '子节点88', 0, sysdate(), 'admin', NULL, NULL, 0); INSERT INTO test_tree(id, parent_id, dept_id, user_id, tree_name, version, create_time, create_by, update_time, update_by, del_flag) VALUES (13, 10, 108, 3, '子节点99', 0, sysdate(), 'admin', NULL, NULL, 0); ================================================ FILE: script/sql/update/oracle/update-4.1-4.2.sql ================================================ ALTER TABLE "SYS_OSS_CONFIG" ADD ("DOMAIN" VARCHAR2(255)); COMMENT ON COLUMN "SYS_OSS_CONFIG"."DOMAIN" IS '自定义域名'; update sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1; update sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2; update sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3; update sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4; insert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', 'N', '', '1', '', NULL, 'admin', sysdate, 'admin', sysdate, ''); ALTER TABLE "GEN_TABLE_COLUMN" MODIFY ("TABLE_ID" NUMBER(20,0)); ================================================ FILE: script/sql/update/oracle/update-4.2-4.3.sql ================================================ insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate, '', null, '缓存列表菜单'); delete from sys_menu WHERE menu_id = 116; update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4; insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate, '', null, ''); insert into sys_role_menu values ('2', '1050'); insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate, '', null, '其他操作'); ================================================ FILE: script/sql/update/oracle/update-4.3-4.4.sql ================================================ ALTER TABLE "SYS_OSS_CONFIG" ADD ("ACCESS_POLICY" CHAR(1) DEFAULT '1' NOT NULL); COMMENT ON COLUMN "SYS_OSS_CONFIG"."ACCESS_POLICY" IS '桶权限类型(0=private 1=public 2=custom)'; ================================================ FILE: script/sql/update/postgres/update-4.1-4.2.sql ================================================ ALTER TABLE "sys_oss_config" ADD COLUMN "domain" varchar(255); COMMENT ON COLUMN "sys_oss_config"."domain" IS '自定义域名'; update sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1; update sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2; update sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3; update sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4; insert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', 'N', '', '1', '', 'admin', now(), 'admin', now(), NULL, ''); ================================================ FILE: script/sql/update/postgres/update-4.2-4.3.sql ================================================ insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', now(), '', null, '缓存列表菜单'); delete from sys_menu WHERE menu_id = 116; update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4; insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', now(), '', null, ''); insert into sys_role_menu values ('2', '1050'); -- 字符串自动转时间 避免框架时间查询报错问题 create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$ select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss'); $$ language sql strict ; create cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT; insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', now(), '', null, '其他操作'); ================================================ FILE: script/sql/update/postgres/update-4.3-4.4.sql ================================================ ALTER TABLE "sys_oss_config" ADD COLUMN "access_policy" char(1) NOT NULL DEFAULT '1'::bpchar; COMMENT ON COLUMN "sys_oss_config"."access_policy" IS '桶权限类型(0=private 1=public 2=custom)'; ================================================ FILE: script/sql/update/sqlserver/update-4.1-4.2.sql ================================================ ALTER TABLE [sys_oss_config] ADD [domain] nvarchar(255) DEFAULT '' NULL GO EXEC sp_addextendedproperty 'MS_Description', N'自定义域名', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'domain' GO UPDATE [sys_oss_config] SET [access_key] = N'ruoyi', [secret_key] = N'ruoyi123', [endpoint] = N'127.0.0.1:9000' WHERE [oss_config_id] = 1 GO UPDATE [sys_oss_config] SET [endpoint] = N's3-cn-north-1.qiniucs.com' WHERE [oss_config_id] = 2 GO UPDATE [sys_oss_config] SET [endpoint] = N'oss-cn-beijing.aliyuncs.com' WHERE [oss_config_id] = 3 GO UPDATE [sys_oss_config] SET [endpoint] = N'cos.ap-beijing.myqcloud.com' WHERE [oss_config_id] = 4 GO INSERT INTO [sys_oss_config] ([oss_config_id], [config_key], [access_key], [secret_key], [bucket_name], [prefix], [endpoint], [domain], [is_https], [region], [status], [ext1], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (N'5', N'image', N'ruoyi', N'ruoyi123', N'ruoyi', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'', N'admin', getdate(), N'admin', getdate(), NULL) GO ALTER TABLE [gen_table_column] ALTER COLUMN [table_id] bigint NULL GO ================================================ FILE: script/sql/update/sqlserver/update-4.2-4.3.sql ================================================ INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (112, N'缓存列表', 2, 6, N'cacheList', N'monitor/cache/list', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis-list', N'admin', getdate(), N'', NULL, N'缓存列表菜单') GO delete from sys_menu WHERE menu_id = 116; GO update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4 GO INSERT [sys_menu] ([menu_id], [menu_name], [parent_id], [order_num], [path], [component], [query_param], [is_frame], [is_cache], [menu_type], [visible], [status], [perms], [icon], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', N'admin', getdate(), N'', null, N'') GO INSERT [sys_role_menu] ([role_id], [menu_id]) VALUES (2, 1050) GO INSERT [sys_dict_data] ([dict_code], [dict_sort], [dict_label], [dict_value], [dict_type], [css_class], [list_class], [is_default], [status], [create_by], [create_time], [update_by], [update_time], [remark]) VALUES (29, 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', N'0', N'admin', getdate(), N'', NULL, N'其他操作'); GO ================================================ FILE: script/sql/update/sqlserver/update-4.3-4.4.sql ================================================ ALTER TABLE [sys_oss_config] ADD [access_policy] nchar(1) DEFAULT ('1') NOT NULL GO EXEC sp_addextendedproperty 'MS_Description', N'桶权限类型(0=private 1=public 2=custom)', 'SCHEMA', N'dbo', 'TABLE', N'sys_oss_config', 'COLUMN', N'access_policy' GO ================================================ FILE: script/sql/update/update-3.X-4.0.sql ================================================ ALTER TABLE `sys_user` MODIFY COLUMN `user_type` varchar(10) DEFAULT 'sys_user' COMMENT '用户类型(sys_user系统用户)' AFTER `nick_name`; UPDATE `sys_user` SET `nick_name` = '疯狂的狮子Li', `user_type` = 'sys_user', `email` = 'crazyLionLi@163.com' WHERE `user_id` = 1; UPDATE `sys_user` SET `user_name` = 'lionli', `nick_name` = '疯狂的狮子Li', `user_type` = 'sys_user', `email` = 'crazyLionLi@163.com' WHERE `user_id` = 2; UPDATE `sys_user` SET `nick_name` = '本部门及以下 密码666666', `user_type` = 'sys_user', `password` = '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne' WHERE `user_id` = 3; UPDATE `sys_user` SET `nick_name` = '仅本人 密码666666', `user_type` = 'sys_user', `password` = '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne' WHERE `user_id` = 4; ================================================ FILE: script/sql/update/update-4.0-4.1.sql ================================================ alter table sys_menu change query query_param varchar(255) default null comment '路由参数'; alter table sys_dept modify column ancestors varchar(500) null default '' comment '祖级列表'; ================================================ FILE: script/sql/update/update-4.1-4.2.sql ================================================ alter table sys_oss_config add column domain varchar(255) null default '' COMMENT '自定义域名'; update sys_oss_config set endpoint = '127.0.0.1:9000' where oss_config_id = 1; update sys_oss_config set endpoint = 's3-cn-north-1.qiniucs.com', region = '' where oss_config_id = 2; update sys_oss_config set endpoint = 'oss-cn-beijing.aliyuncs.com' where oss_config_id = 3; update sys_oss_config set endpoint = 'cos.ap-beijing.myqcloud.com' where oss_config_id = 4; insert into sys_oss_config values (5, 'image', 'ruoyi', 'ruoyi123', 'ruoyi', 'image', '127.0.0.1:9000', 'N', '', '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL, ''); alter table gen_table_column modify column table_id bigint(0) null default null COMMENT '归属表编号'; alter table sys_notice modify column notice_id bigint(0) not null COMMENT '公告ID'; alter table sys_config modify column config_id bigint(0) not null COMMENT '参数主键'; ================================================ FILE: script/sql/update/update-4.2-4.3.sql ================================================ insert into sys_menu values('112', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单'); delete from sys_menu WHERE menu_id = 116; update sys_config set config_key = 'sys.account.captchaEnabled' where config_id = 4; insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, ''); insert into sys_role_menu values ('2', '1050'); insert into sys_dict_data values(29, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作'); ================================================ FILE: script/sql/update/update-4.3-4.4.sql ================================================ ALTER TABLE sys_oss_config ADD COLUMN access_policy char(1) NOT NULL DEFAULT 1 COMMENT '桶权限类型(0=private 1=public 2=custom)' AFTER region;