Repository: Ginyi3705/ginyi-spring-vue Branch: master Commit: 6c07298cd34c Files: 329 Total size: 34.0 MB Directory structure: gitextract_klywiall/ ├── .gitignore ├── LICENSE ├── README.md ├── ginyi-springboot/ │ ├── .gitignore │ ├── README.md │ ├── ginyi-common/ │ │ ├── ginyi-common-annotation/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi.common.annotation/ │ │ │ ├── Anonymous.java │ │ │ ├── CreateBy.java │ │ │ ├── CreateTime.java │ │ │ ├── Log.java │ │ │ ├── RepeatSubmit.java │ │ │ ├── UpdateBy.java │ │ │ └── UpdateTime.java │ │ ├── ginyi-common-constants/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── constant/ │ │ │ ├── CacheConstants.java │ │ │ ├── CommonMessageConstants.java │ │ │ ├── HttpStatus.java │ │ │ └── UserConstants.java │ │ ├── ginyi-common-enums/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── enums/ │ │ │ ├── BusinessStatus.java │ │ │ ├── BusinessType.java │ │ │ ├── HttpMethod.java │ │ │ └── OperatorType.java │ │ ├── ginyi-common-exception/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── exception/ │ │ │ ├── CommonException.java │ │ │ ├── UnPermissionException.java │ │ │ ├── UserPasswordNotMatchException.java │ │ │ ├── UserPasswordRetryLimitExceedException.java │ │ │ └── handler/ │ │ │ └── GlobalExceptionHandler.java │ │ ├── ginyi-common-mysql/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── mysql/ │ │ │ ├── MyPage.java │ │ │ ├── config/ │ │ │ │ └── MyBatisPlusInterceptorConfig.java │ │ │ ├── handler/ │ │ │ │ ├── JacksonArrayTypeHandler.java │ │ │ │ └── MyMetaObjectHandler.java │ │ │ └── interceptor/ │ │ │ └── MyInterceptor.java │ │ ├── ginyi-common-redis/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── redis/ │ │ │ ├── cache/ │ │ │ │ └── RedisCache.java │ │ │ └── config/ │ │ │ └── RedisConfig.java │ │ ├── ginyi-common-result/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── result/ │ │ │ ├── CommonResult.java │ │ │ └── StateCode.java │ │ ├── ginyi-common-swagger/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── swagger/ │ │ │ ├── AddGroup.java │ │ │ ├── Swagger2Config.java │ │ │ └── UpdateGroup.java │ │ ├── ginyi-common-utils/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── common/ │ │ │ └── utils/ │ │ │ ├── Constants.java │ │ │ ├── DateUtils.java │ │ │ ├── EmailUtils.java │ │ │ ├── LogUtils.java │ │ │ ├── LunarCalendarUtil.java │ │ │ ├── MessageUtils.java │ │ │ ├── NumberUtils.java │ │ │ ├── ServletUtils.java │ │ │ ├── StringUtils.java │ │ │ ├── Threads.java │ │ │ ├── enums/ │ │ │ │ └── UserStatus.java │ │ │ ├── http/ │ │ │ │ ├── HttpHelper.java │ │ │ │ └── HttpUtils.java │ │ │ ├── ip/ │ │ │ │ ├── AddressUtils.java │ │ │ │ └── IpUtils.java │ │ │ ├── spring/ │ │ │ │ └── SpringUtils.java │ │ │ ├── text/ │ │ │ │ ├── CharsetKit.java │ │ │ │ ├── Convert.java │ │ │ │ └── StrFormatter.java │ │ │ └── uuid/ │ │ │ ├── IdUtils.java │ │ │ └── UUID.java │ │ └── pom.xml │ ├── ginyi-framework/ │ │ ├── ginyi-framework-core/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── framework/ │ │ │ └── core/ │ │ │ └── config/ │ │ │ ├── AppConfig.java │ │ │ └── GinyiConfig.java │ │ ├── ginyi-framework-security/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── framework/ │ │ │ └── security/ │ │ │ ├── aspectj/ │ │ │ │ └── LogAspect.java │ │ │ ├── config/ │ │ │ │ ├── ResourcesConfig.java │ │ │ │ ├── SecurityConfig.java │ │ │ │ └── ThreadPoolConfig.java │ │ │ ├── context/ │ │ │ │ ├── AuthenticationContextHolder.java │ │ │ │ └── PermissionContextHolder.java │ │ │ ├── filter/ │ │ │ │ ├── JwtAuthenticationTokenFilter.java │ │ │ │ ├── PropertyPreExcludeFilter.java │ │ │ │ └── RepeatedlyRequestWrapper.java │ │ │ ├── handle/ │ │ │ │ ├── AuthenticationEntryPointImpl.java │ │ │ │ └── LogoutSuccessHandlerImpl.java │ │ │ ├── interceptor/ │ │ │ │ ├── PreviewEnvInterceptor.java │ │ │ │ ├── RepeatSubmitInterceptor.java │ │ │ │ └── impl/ │ │ │ │ └── SameUrlDataInterceptor.java │ │ │ ├── manager/ │ │ │ │ ├── AsyncManager.java │ │ │ │ └── factory/ │ │ │ │ └── AsyncFactory.java │ │ │ ├── properties/ │ │ │ │ └── PermitAllUrlProperties.java │ │ │ ├── service/ │ │ │ │ ├── ISysNoticeServiceImpl.java │ │ │ │ ├── MonitorServiceImpl.java │ │ │ │ ├── PermissionService.java │ │ │ │ ├── SysConfigServiceImpl.java │ │ │ │ ├── SysDeptServiceImpl.java │ │ │ │ ├── SysLogServiceImpl.java │ │ │ │ ├── SysLoginLogServiceImpl.java │ │ │ │ ├── SysLoginServiceImpl.java │ │ │ │ ├── SysMenuServiceImpl.java │ │ │ │ ├── SysMonitorServiceImpl.java │ │ │ │ ├── SysOperationLogServiceImpl.java │ │ │ │ ├── SysPasswordServiceImpl.java │ │ │ │ ├── SysPermissionServiceImpl.java │ │ │ │ ├── SysPostServiceImpl.java │ │ │ │ ├── SysRoleServiceImpl.java │ │ │ │ ├── SysUserServiceImpl.java │ │ │ │ ├── SysVerifyServiceImpl.java │ │ │ │ ├── TokenServiceImpl.java │ │ │ │ └── UserDetailsServiceImpl.java │ │ │ └── utils/ │ │ │ └── SecurityUtils.java │ │ ├── ginyi-framework-websocket/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── ginyi/ │ │ │ └── framework/ │ │ │ └── websocket/ │ │ │ ├── WebSocket.java │ │ │ └── config/ │ │ │ └── WebSocketConfig.java │ │ └── pom.xml │ ├── ginyi-server/ │ │ ├── ginyi-server-admin/ │ │ │ ├── Dockerfile │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── ginyi/ │ │ │ │ └── server/ │ │ │ │ └── admin/ │ │ │ │ ├── AdminApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ ├── SysDeptController.java │ │ │ │ │ ├── SysLogController.java │ │ │ │ │ ├── SysLoginController.java │ │ │ │ │ ├── SysMenuController.java │ │ │ │ │ ├── SysMonitorController.java │ │ │ │ │ ├── SysNoticeController.java │ │ │ │ │ ├── SysOnlineController.java │ │ │ │ │ ├── SysPostController.java │ │ │ │ │ ├── SysRoleController.java │ │ │ │ │ ├── SysUserController.java │ │ │ │ │ └── SysVerifyController.java │ │ │ │ └── swagger/ │ │ │ │ └── AdminSwagger2Config.java │ │ │ └── resources/ │ │ │ ├── config/ │ │ │ │ └── application.yml │ │ │ └── static/ │ │ │ └── i18n/ │ │ │ └── messages.properties │ │ ├── ginyi-server-common/ │ │ │ └── pom.xml │ │ ├── ginyi-server-web/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── ginyi/ │ │ │ │ └── server/ │ │ │ │ └── web/ │ │ │ │ ├── WebApplication.java │ │ │ │ ├── controller/ │ │ │ │ │ └── Test.java │ │ │ │ └── swagger/ │ │ │ │ └── WebSwagger2Config.java │ │ │ └── resources/ │ │ │ └── config/ │ │ │ └── application.yml │ │ └── pom.xml │ ├── ginyi-system/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── ginyi/ │ │ │ └── system/ │ │ │ ├── domain/ │ │ │ │ ├── BaseEntity.java │ │ │ │ ├── LoginUser.java │ │ │ │ ├── Sys.java │ │ │ │ ├── SysConfig.java │ │ │ │ ├── SysCpu.java │ │ │ │ ├── SysDept.java │ │ │ │ ├── SysFile.java │ │ │ │ ├── SysJvm.java │ │ │ │ ├── SysLogLogin.java │ │ │ │ ├── SysLogOperation.java │ │ │ │ ├── SysMemory.java │ │ │ │ ├── SysMenu.java │ │ │ │ ├── SysNotice.java │ │ │ │ ├── SysPost.java │ │ │ │ ├── SysRole.java │ │ │ │ ├── SysServer.java │ │ │ │ ├── SysUser.java │ │ │ │ └── model/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── CacheDto.java │ │ │ │ │ ├── DeptDto.java │ │ │ │ │ ├── LoginDto.java │ │ │ │ │ ├── MenuDto.java │ │ │ │ │ ├── NoticeDto.java │ │ │ │ │ ├── PostDto.java │ │ │ │ │ ├── RegisterDto.java │ │ │ │ │ ├── RoleDto.java │ │ │ │ │ └── UserDto.java │ │ │ │ └── vo/ │ │ │ │ ├── BaseVo.java │ │ │ │ ├── CacheKeyVo.java │ │ │ │ ├── CacheVo.java │ │ │ │ ├── DeptVo.java │ │ │ │ ├── LoginVo.java │ │ │ │ ├── NoticeVo.java │ │ │ │ ├── PostVo.java │ │ │ │ ├── RoleVo.java │ │ │ │ ├── SessionUserVo.java │ │ │ │ └── UserVo.java │ │ │ ├── mapper/ │ │ │ │ ├── SysConfigMapper.java │ │ │ │ ├── SysDeptMapper.java │ │ │ │ ├── SysLogininforMapper.java │ │ │ │ ├── SysMenuMapper.java │ │ │ │ ├── SysNoticeMapper.java │ │ │ │ ├── SysOperLogMapper.java │ │ │ │ ├── SysPostMapper.java │ │ │ │ ├── SysRoleMapper.java │ │ │ │ └── SysUserMapper.java │ │ │ └── service/ │ │ │ ├── ISysConfigService.java │ │ │ ├── ISysDeptService.java │ │ │ ├── ISysLogService.java │ │ │ ├── ISysLoginService.java │ │ │ ├── ISysLogininforService.java │ │ │ ├── ISysMenuService.java │ │ │ ├── ISysMonitorService.java │ │ │ ├── ISysNoticeService.java │ │ │ ├── ISysOnlineService.java │ │ │ ├── ISysOperLogService.java │ │ │ ├── ISysPasswordService.java │ │ │ ├── ISysPermissionService.java │ │ │ ├── ISysPostService.java │ │ │ ├── ISysRoleService.java │ │ │ ├── ISysUserService.java │ │ │ ├── ITokenService.java │ │ │ └── IVerifyService.java │ │ └── resources/ │ │ ├── application.yml │ │ ├── logging-config.xml │ │ └── mapper/ │ │ ├── SysDeptMapper.xml │ │ ├── SysMenuMapper.xml │ │ ├── SysNoticeMapper.xml │ │ ├── SysPostMapper.xml │ │ ├── SysRoleMapper.xml │ │ └── SysUserMapper.xml │ ├── pom.xml │ └── sql/ │ ├── ginyi-spring-vue-mongo.sql │ └── ginyi-spring-vue-mysql.sql └── ginyi-vue3/ ├── .editorconfig ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src/ │ ├── App.vue │ ├── api/ │ │ ├── controller/ │ │ │ ├── deptController.ts │ │ │ ├── logController.ts │ │ │ ├── menuController.ts │ │ │ ├── monitorController.ts │ │ │ ├── onlineController.ts │ │ │ ├── postController.ts │ │ │ ├── roleController.ts │ │ │ └── userController.ts │ │ ├── index.ts │ │ └── useRequest.tsx │ ├── components/ │ │ ├── commonForm/ │ │ │ ├── index.vue │ │ │ ├── props.ts │ │ │ └── useCommonForm.ts │ │ ├── commonModal/ │ │ │ ├── index.vue │ │ │ ├── props.ts │ │ │ └── useCommonModal.ts │ │ ├── commonTable/ │ │ │ ├── index.vue │ │ │ ├── props.ts │ │ │ └── useCommonTable.ts │ │ └── monacoEditor/ │ │ ├── index.vue │ │ └── monacoEditorType.ts │ ├── config/ │ │ ├── console.log.ts │ │ ├── eventBus.ts │ │ └── setting.ts │ ├── dictionary/ │ │ ├── useDynamicDict.ts │ │ └── useStaticDict.ts │ ├── directives/ │ │ ├── draggable.ts │ │ ├── focus.ts │ │ ├── index.ts │ │ └── loading.ts │ ├── enums/ │ │ ├── storeKeyEnums.ts │ │ └── tableActionEnums.ts │ ├── hooks/ │ │ ├── useColor.ts │ │ ├── useCommonColums.ts │ │ ├── useDebthro.ts │ │ ├── useFormat.ts │ │ ├── useMenu.ts │ │ ├── useObject.ts │ │ ├── usePagination.ts │ │ ├── usePending.ts │ │ ├── useStorage.ts │ │ └── useTree.ts │ ├── interface/ │ │ └── modules/ │ │ └── system/ │ │ └── index.ts │ ├── layout/ │ │ ├── header/ │ │ │ ├── breadcrumb.vue │ │ │ ├── index.vue │ │ │ └── personnel.vue │ │ ├── index.vue │ │ ├── logo/ │ │ │ └── index.vue │ │ ├── menu/ │ │ │ └── index.vue │ │ ├── tabs/ │ │ │ └── index.vue │ │ └── theme/ │ │ └── index.vue │ ├── main.ts │ ├── plugins/ │ │ ├── naive-ui/ │ │ │ └── common.tsx │ │ └── pinia/ │ │ └── piniaPlugin.ts │ ├── router/ │ │ └── index.ts │ ├── store/ │ │ ├── index.ts │ │ └── modules/ │ │ ├── useRouterStore.ts │ │ ├── useSystemStore.ts │ │ └── useUserStore.ts │ ├── style/ │ │ └── index.css │ ├── views/ │ │ ├── 404/ │ │ │ └── index.vue │ │ ├── home/ │ │ │ └── index.vue │ │ ├── login/ │ │ │ ├── index.vue │ │ │ ├── loginForm.vue │ │ │ └── registerForm.vue │ │ └── pages/ │ │ ├── monitor/ │ │ │ ├── cache/ │ │ │ │ └── index.vue │ │ │ ├── cacheList/ │ │ │ │ ├── columns.tsx │ │ │ │ └── index.vue │ │ │ ├── data/ │ │ │ │ └── index.vue │ │ │ ├── online/ │ │ │ │ ├── columns.tsx │ │ │ │ └── index.vue │ │ │ ├── service/ │ │ │ │ ├── columns.tsx │ │ │ │ └── index.vue │ │ │ └── task/ │ │ │ └── index.vue │ │ ├── order/ │ │ │ └── index.vue │ │ ├── product/ │ │ │ └── index.vue │ │ ├── system/ │ │ │ ├── department/ │ │ │ │ ├── columns.tsx │ │ │ │ ├── deptEditForm.vue │ │ │ │ ├── deptQueryForm.vue │ │ │ │ └── index.vue │ │ │ ├── dict/ │ │ │ │ └── index.vue │ │ │ ├── log/ │ │ │ │ ├── login/ │ │ │ │ │ ├── columns.tsx │ │ │ │ │ └── index.vue │ │ │ │ └── operation/ │ │ │ │ ├── columns.tsx │ │ │ │ ├── index.vue │ │ │ │ └── useColumns.ts │ │ │ ├── menu/ │ │ │ │ ├── columns.tsx │ │ │ │ ├── index.vue │ │ │ │ ├── menuEditForm.vue │ │ │ │ ├── menuQueryForm.vue │ │ │ │ └── useColumns.ts │ │ │ ├── notice/ │ │ │ │ └── index.vue │ │ │ ├── params/ │ │ │ │ └── index.vue │ │ │ ├── position/ │ │ │ │ ├── columns.tsx │ │ │ │ ├── index.vue │ │ │ │ ├── postEditForm.vue │ │ │ │ └── postQueryForm.vue │ │ │ ├── role/ │ │ │ │ ├── columns.tsx │ │ │ │ ├── index.vue │ │ │ │ ├── roleEditForm.vue │ │ │ │ └── roleQueryForm.vue │ │ │ └── user/ │ │ │ ├── columns.tsx │ │ │ ├── index.vue │ │ │ ├── useColumns.ts │ │ │ ├── userEditForm.vue │ │ │ └── userQueryForm.vue │ │ └── systools/ │ │ └── code/ │ │ └── index.vue │ └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── web-types.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ### gradle ### .gradle /build/ !gradle/wrapper/gradle-wrapper.jar ### STS ### .settings/ .apt_generated .classpath .factorypath .project .settings .springBeans bin/ ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr rebel.xml /ginyi-springboot/.idea ### NetBeans ### nbproject/private/ build/ nbbuild/ nbdist/ .nb-gradle/ ### maven ### target/ *.war *.ear *.zip *.tar *.tar.gz *.versionsBackup ### vscode ### .vscode ### logs ### /logs/ *.log ### temp ignore ### *.cache *.diff *.patch *.tmp *.java~ *.properties~ *.xml~ ### system ignore ### .DS_Store Thumbs.db Servers .metadata /**/*.idea/** ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: README.md ================================================

logo

Ginyi

基于对优秀开源项目RuoYi-Vue的喜爱,决定重构若依,打造属于自己的快速开发平台!

————向优秀致敬,向榜样学习!

### 平台简介 基于`SpringBoot`和`Vue3`,搭配`Naive UI`组件库,模块清晰,界面美观,可自主选择主题色,提高视觉舒适度!部分功能尚未实现,有时间将持续更新,欢迎Star⭐! ### 特别鸣谢 - [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue?_from=gitee_search) - [Naive UI](https://www.naiveui.com/) ### 项目地址 演示地址:http://114.132.120.190:3800 (`仅PC端`) 接口地址:http://114.132.120.190:8066/doc.html | 演示账号 / 密码 | 说明 | | ------------------ | ---------------------------- | | `admin / 123456` | 超级管理员,具有全部权限 | | `Ginyi / 123456` | 系统管理员,具有系统管理权限 | | `order / 123456` | 订单管理员,具有订单管理权限 | | `product / 123456` | 商品管理员,具有商品管理权限 | **注:**学生机服务器,5月份到期~ 请珍惜公用资源,请勿攻击~ 拜托拜托!😘😘😘 ### 帮助文档 - 没有~不想写文档...如果`Star`数量破`1k`,可以考虑一下! - 可以参考`Ruoyi`的文档 http://doc.ruoyi.vip/(仅后端) ### 技术选型 | 前端 | 后端 | | ---------------------------------------- | ------------------------------- | | `Node` 项目环境基础 | `Maven` 构建多模块项目 | | `Vite4` 构建项目 | `Springboot` 项目主框架 | | `Vue3` 项目主框架 | `MySQL` 关系型数据库 | | `TypeScript` 全量TS编写,搭配TSX模板渲染 | `MongoDB` 文档型数据库 | | `Naive UI` 组件库 | `Redis` 基于内存数据库 | | `Pinia` 全局状态管理 | `SpringSecurity & JWT` 权限框架 | | `Axios` 网络请求库 | `MyBatis-Plus` ORM框架 | | `Monaco` 代码编辑器(vscode) | `Hutool` 工具类 | | `...` | `...` | ### 内置功能 | 功能 | 介绍 | Ruoyi | Ginyi | | ------------ | ------------------------------------------------------------ | ----- | ----- | | `用户管理` | 用户是系统操作者,该功能主要完成系统用户配置 | ⭕ | ⭕ | | `部门管理` | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 | ⭕ | ⭕ | | `岗位管理` | 配置系统用户所属担任职务 | ⭕ | ⭕ | | `菜单管理` | 配置系统菜单,操作权限,按钮权限标识等 | ⭕ | ⭕ | | `角色管理` | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | ⭕ | ⭕ | | `字典管理` | 对系统中经常使用的一些较为固定的数据进行维护 | ⭕ | ❎ | | `参数管理` | 对系统动态配置常用参数 | ⭕ | ❎ | | `通知公告` | 系统通知公告信息发布维护 | ⭕ | ❎ | | `操作日志` | 系统正常操作日志记录和查询;系统异常信息日志记录和查询 | ⭕ | ⭕ | | `登录日志` | 系统登录日志记录查询包含登录异常 | ⭕ | ⭕ | | `在线用户` | 当前系统中活跃用户状态监控 | ⭕ | ⭕ | | `定时任务` | 在线(添加、修改、删除)任务调度包含执行结果日志 | ⭕ | ❎ | | `代码生成` | 前后端代码的生成(java、html、xml、sql)支持CRUD下载 | ⭕ | ❎ | | `系统接口` | 根据业务代码自动生成相关的api接口文档 | ⭕ | ❎ | | `服务监控` | 监视当前系统CPU、内存、磁盘、堆栈等相关信息 | ⭕ | ❎ | | `缓存监控` | 对系统的缓存信息查询,命令统计等 | ⭕ | ❎ | | `连接池监视` | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 | ⭕ | ❎ | **备注:** - 尚未实现的功能,有时间将继续更新! ### 效果预览
================================================ FILE: ginyi-springboot/.gitignore ================================================ ### gradle ### .gradle /build/ !gradle/wrapper/gradle-wrapper.jar ### STS ### .settings/ .apt_generated .classpath .factorypath .project .settings .springBeans bin/ ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr rebel.xml ### NetBeans ### nbproject/private/ build/ nbbuild/ nbdist/ .nb-gradle/ ### maven ### target/ *.war *.ear *.zip *.tar *.tar.gz *.versionsBackup ### vscode ### .vscode ### logs ### /logs/ *.log ### temp ignore ### *.cache *.diff *.patch *.tmp *.java~ *.properties~ *.xml~ ### system ignore ### .DS_Store Thumbs.db Servers .metadata ================================================ FILE: ginyi-springboot/README.md ================================================ ### 项目地址 接口文档:http://114.132.120.190:8066/doc.html 代码仓库:https://gitee.com/Ginyi/ginyi-spring-vue ### 帮助文档 - 没有~不想写文档...如果`Star`数量破`1k`,可以考虑一下! - 可以参考`Ruoyi`的文档 http://doc.ruoyi.vip/ ### 项目特色 - 使用主流技术栈`SpringBoot`、`Mybatis-plus`、`Redis`、`MySQL`、`MongoDB`等,易上手! - 基于`SpringSecurity`安全框架实现`RBAC`的权限模式,易扩展易维护! - 基于`Maven`多模块,可在上层应用中创建多个服务!模块分明,各司其职! - 内置多个后台系统常用功能,搭配完善的接口文档: - 系统管理:`用户管理`、`部门管理`、`岗位管理`、`角色管理`、`菜单管理`等 - 日志管理:`登录日志`、`操作日志`、`在线用户`等 ### 前置知识 **重点:**使用本项目时,你需要具备以下基础知识: 1. `SpringBoot`框架基础,熟悉`MVC`分层的开发模式! 2. `Maven`基础,掌握多模块项目下,服务间的依赖引用! 3. `Redis`、`MongoDB`中间件、`Mybatis`和`Mybatis-plus`等`ORM`框架的使用! ### 项目环境 **注意:**使用本项目时,为了你能愉快地进行开发,请尽可能使用以下环境进行开发: 1. `JDK v1.8` 2. `Maven v3.6.3` 3. `MySQL v8.0` 4. `IntelliJ IDEA` ### 项目结构 ```bash ginyi-springboot | ├─ ginyi-common # 通用模块 │ ├─ ginyi-common-annotation # 通用注解 │ ├─ ginyi-common-constants # 通用常量 │ ├─ ginyi-common-enums # 通用枚举 │ ├─ ginyi-common-exception # 通用异常处理 │ ├─ ginyi-common-mysql # 通用MySql配置 │ ├─ ginyi-common-redis # 通用Redis配置 │ ├─ ginyi-common-result # 通用结果集 │ ├─ ginyi-common-swagger # 通用在线接口文档配置 │ └─ ginyi-common-utils # 通用工具类 | ├─ ginyi-framework # 框架模块 │ ├─ ginyi-framework-core # 框架核心 │ ├─ ginyi-framework-security # 安全框架 │ └─ ginyi-framework-websocket # 实时通讯框架 | ├─ ginyi-server # 服务模块(对外提供api接口) │ ├─ ginyi-server-admin # 后台系统端(包含系统接口) │ ├─ ginyi-server-common # 通用服务 │ └─ ginyi-server-web # 前台应用端 | ├─ ginyi-system # 系统服务模块(包含yaml配置、系统Entity、系统Service、系统Mapper等) | └─ sql # 数据库文件 ``` ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-annotation 8 8 com.ginyi ginyi-common-enums ${ginyi.version} ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/Anonymous.java ================================================ package ginyi.common.annotation; import java.lang.annotation.*; /** * 匿名访问不鉴权注解 * * @author ruoyi */ @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Anonymous { } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/CreateBy.java ================================================ package ginyi.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自动设置创建人 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface CreateBy { String value() default ""; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/CreateTime.java ================================================ package ginyi.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自动设置创建时间 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface CreateTime { String value() default ""; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/Log.java ================================================ package ginyi.common.annotation; import ginyi.common.enums.BusinessType; import ginyi.common.enums.OperatorType; import java.lang.annotation.*; /** * 自定义操作日志记录注解 * * @author ruoyi */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** * 模块 */ public String title() default ""; /** * 功能 */ public BusinessType businessType() default BusinessType.OTHER; /** * 操作人类别 */ public OperatorType operatorType() default OperatorType.MANAGE; /** * 是否保存请求的参数 */ public boolean isSaveRequestData() default true; /** * 是否保存响应的参数 */ public boolean isSaveResponseData() default true; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/RepeatSubmit.java ================================================ package ginyi.common.annotation; import java.lang.annotation.*; /** * 自定义注解防止表单重复提交 * * @author ruoyi */ @Inherited @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RepeatSubmit { /** * 间隔时间(ms),小于此时间视为重复提交 */ public int interval() default 5000; /** * 提示消息 */ public String message() default "不允许重复提交,请稍候再试"; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/UpdateBy.java ================================================ package ginyi.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自动设置修改人 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface UpdateBy { String value() default ""; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-annotation/src/main/java/ginyi.common.annotation/UpdateTime.java ================================================ package ginyi.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自动设置修改时间 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface UpdateTime { String value() default ""; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-constants/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-constants 8 8 ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/CacheConstants.java ================================================ package ginyi.common.constant; /** * 缓存的key 常量 * * @author ruoyi */ public class CacheConstants { /** * 参数管理 cache key */ public static final String SYS_CONFIG_KEY = "sys_config:"; /** * 验证码 redis key */ public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; /** * 登录用户 redis key */ public static final String LOGIN_TOKEN_KEY = "login_tokens:"; /** * 登录账户密码错误次数 redis key */ public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; /** * 防重提交 redis key */ public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; /** 防止恶意请求 */ public static final String API_REQUEST = "api_request:"; /** 菜单前缀,用于批量删除 */ public static final String MENU_KEY_PREFIX = "menu_*"; /** 菜单列表 */ public static final String MENU_LIST_KEY = "menu_list"; /** 用户菜单的菜单列表 */ public static final String MENU_USER_LIST_KEY = "menu_user_list:"; /** 菜单不存在的key */ public static final String MENU_NOT_EXIST_KEY = "menu_not_exist:"; /** 菜单详情 */ public static final String MENU_DETAILS_BY_ID_KEY = "menu_details_by_id:"; /** 用户前缀,用于批量删除 */ public static final String USER_KEY_PREFIX = "user_*"; /** 用户列表的key */ public static final String USER_LIST_KEY = "user_list"; /** 用户不存在的key */ public static final String USER_NOT_EXIST_KEY = "user_not_exist:"; /** 用户详情 */ public static final String USER_DETAILS_BY_USERID_KEY = "user_details_by_userId:"; /** 部门前缀,用于批量删除 */ public static final String DEPT_KEY_PREFIX = "dept_*"; /** 部门列表 */ public static final String DEPT_LIST_KEY = "dept_list"; /** 部门不存在的key */ public static final String DEPT_NOT_EXIST_KEY = "dept_not_exist:"; /** 部门详情 */ public static final String DEPT_DETAILS_BY_DEPTID_KEY = "dept_details_by_deptId:"; /** 同分支下部门名称已被使用 */ public static final String DEPT_NAME_USED_KEY = "dept_name_used_key:"; /** 岗位前缀,用于批量删除 */ public static final String POST_KEY_PREFIX = "post_*"; /** 岗位列表 */ public static final String POST_LIST_KEY = "post_list"; /** 岗位不存在大的key */ public static final String POST_NOT_EXIST_KEY = "post_not_exist:"; /** 岗位详情 */ public static final String POST_DETAILS_BY_POSTID_KEY = "post_details_by_postId:"; /** 角色前缀,用于批量删除 */ public static final String ROLE_KEY_PREFIX = "role_*"; /** 角色列表 */ public static final String ROLE_LIST_KEY = "role_list"; /** 角色不存在的key */ public static final String ROLE_NOT_EXIST_KEY = "role_not_exist:"; /** 角色名已被使用 */ public static final String ROLE_NAME_USED_KEY = "role_name_used:"; /** 角色编码已被使用 */ public static final String ROLE_CODE_USED_KEY = "role_code_used:"; /** 角色详情 */ public static final String ROLE_DETAILS_BY_ROLEID_KEY = "role_details_by_roleId:"; /** 通知公告不存在的key */ public static final String NOTICE_NOT_EXIST_KEY = "notice_not_exist:"; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/CommonMessageConstants.java ================================================ package ginyi.common.constant; /** * 错误信息常量 */ public class CommonMessageConstants { public static final String LOGIN_SUCCESS = "登录成功"; public static final String REGISTER_SUCCESS = "注册成功"; public static final String REDIS_KEY_NOT_EXIST = "该Key不存在,请仔细检查!"; public static final String REDIS_VALUE_TYPE_NOT_MATCH = "数据类型不匹配,请联系管理人员!"; public static final String SYS_ERROR = "请联系管理人员!"; public static final String SYS_AUTHENTICATION_VALID = "权限不足,无法访问系统资源!"; public static final String SYS_REQUEST_ILLEGAL = "请求参数错误,请仔细检查!"; public static final String SYS_CAPTCHA_NOT_EXIST = "验证码不能为空!"; public static final String SYS_PREVIEW_ENV = "演示环境不允许进行此操作!"; public static final String SYS_BED_REQUEST = "恶意请求!该请求已被终止!"; public static final String UPLOAD_FILE_ERROR = "文件上传业务异常"; public static final String UPLOAD_SIZE_EXCEED = "单文件大小不得大于5MB,总文件大小不得大于50MB"; public static final String VERIFY_EXPIRE = "验证码已失效!"; public static final String VERiFY_INCORRECT = "验证码错误!"; public static final String USER_PASSWORD_NOT_MATCH = "用户名密码不匹配!"; public static final String USER_EXIST = "用户已存在!"; public static final String USER_NOT_EXIST = "用户不存在!"; public static final String USER_IS_DELETED = "用户已被注销!"; public static final String USER_IS_FORBIDDEN = "用户已被停用!"; public static final String USER_IS_LOCKED = "用户已被停用!"; public static final String USER_NAME_USED = "用户名已被占用!"; public static final String USER_NOT_FOUND = "请求参数错误,请仔细检查!"; public static final String USER_ID_NOT_FOUND = "请求参数错误,用户ID不能为空!"; public static final String USER_STATUS_ILLEGAL = "请求参数错误,用户状态不合法!"; public static final String USER_IDS_ILLEGAL = "通知的用户不能为空!"; public static final String MENU_COMPONENT_NOT_EXIST = "组件路径不存在!"; public static final String MENU_PATH_NOT_EXIST = "路由地址不存在!"; public static final String MENU_NAME_USED = "菜单名称已被使用!"; public static final String MENU_NOT_EXIST = "菜单不存在!"; public static final String MENU_PARENT_NOT_EXIST = "上级菜单不存在!"; public static final String MENU_ID_NOT_FOUND = "请求参数错误,菜单ID不能为空!"; public static final String MENU_STATUS_ILLEGAL = "请求参数错误,菜单状态不合法!"; public static final String POST_NOT_EXIST = "岗位不存在!"; public static final String POST_NAME_USED = "岗位名称已被使用!"; public static final String POST_CODE_USED = "岗位编码已被使用!"; public static final String POST_ID_NOT_FOUND = "请求参数错误,岗位ID不能为空!"; public static final String POST_STATUS_ILLEGAL = "请求参数错误,岗位状态不合法!"; public static final String ROLE_NOT_EXIST = "角色不存在!"; public static final String ROLE_NAME_USED = "角色名称已被使用!"; public static final String ROLE_PERMISSION_CODE_USED = "角色权限字符已被使用!"; public static final String ROLE_MENU_NOT_EXIST = "菜单权限不存在!"; public static final String ROLE_ID_NOT_FOUND = "请求参数错误,角色ID不能为空!"; public static final String ROLE_STATUS_ILLEGAL = "请求参数错误,角色状态不合法!"; public static final String DEPT_NOT_EXIST = "部门不存在!"; public static final String DEPT_NAME_USED = "同分支下部门名称已被使用!"; public static final String DEPT_PARENT_NOT_EXIST = "上级部门不存在!"; public static final String DEPT_ID_NOT_FOUND = "请求参数错误,部门ID不能为空!"; public static final String DEPT_STATUS_ILLEGAL = "请求参数错误,部门状态不合法!"; public static final String NOTICE_NOT_EXIST = "通知公告不存在!"; public static final String NOTICE_CURRENT_USER_NOT_EXIST = "当前用户不存在该消息!"; public static final String NOTICE_HAVE_READ = "当前通知公告已确认,无需二次确认!"; public static final String NOTICE_NOT_ALLOW = "当前通知公告无法被修改!"; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/HttpStatus.java ================================================ package ginyi.common.constant; /** * 返回状态码 * * @author ruoyi */ public class HttpStatus { /** * 操作成功 */ public static final int SUCCESS = 200; /** * 对象创建成功 */ public static final int CREATED = 201; /** * 请求已经被接受 */ public static final int ACCEPTED = 202; /** * 操作已经执行成功,但是没有返回数据 */ public static final int NO_CONTENT = 204; /** * 资源已被移除 */ public static final int MOVED_PERM = 301; /** * 重定向 */ public static final int SEE_OTHER = 303; /** * 资源没有被修改 */ public static final int NOT_MODIFIED = 304; /** * 参数列表错误(缺少,格式不匹配) */ public static final int BAD_REQUEST = 400; /** * 未授权 */ public static final int UNAUTHORIZED = 401; /** * 访问受限,授权过期 */ public static final int FORBIDDEN = 403; /** * 资源,服务未找到 */ public static final int NOT_FOUND = 404; /** * 不允许的http方法 */ public static final int BAD_METHOD = 405; /** * 资源冲突,或者资源被锁 */ public static final int CONFLICT = 409; /** * 不支持的数据,媒体类型 */ public static final int UNSUPPORTED_TYPE = 415; /** * 系统内部错误 */ public static final int ERROR = 500; /** * 接口未实现 */ public static final int NOT_IMPLEMENTED = 501; /** * 系统警告消息 */ public static final int WARN = 601; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-constants/src/main/java/ginyi/common/constant/UserConstants.java ================================================ package ginyi.common.constant; /** * 用户常量信息 * * @author ruoyi */ public class UserConstants { /** * 平台内系统用户的唯一标志 */ public static final String SYS_USER = "SYS_USER"; /** * 当前线程上的用户 */ public static final String CURRENT_USER = "CURRENT_USER"; /** * 正常状态 */ public static final String NORMAL = "0"; /** * 异常状态 */ public static final String EXCEPTION = "1"; /** * 用户封禁状态 */ public static final String USER_DISABLE = "1"; /** * 角色封禁状态 */ public static final String ROLE_DISABLE = "1"; /** * 部门正常状态 */ public static final String DEPT_NORMAL = "0"; /** * 部门停用状态 */ public static final String DEPT_DISABLE = "1"; /** * 字典正常状态 */ public static final String DICT_NORMAL = "0"; /** * 是否为系统默认(是) */ public static final String YES = "Y"; /** * 是否菜单外链(是) */ public static final String YES_FRAME = "0"; /** * 是否菜单外链(否) */ public static final String NO_FRAME = "1"; /** * 菜单类型(目录) */ public static final String TYPE_DIR = "M"; /** * 菜单类型(菜单) */ public static final String TYPE_MENU = "C"; /** * 菜单类型(按钮) */ public static final String TYPE_BUTTON = "F"; /** * Layout组件标识 */ public final static String LAYOUT = "Layout"; /** * ParentView组件标识 */ public final static String PARENT_VIEW = "ParentView"; /** * InnerLink组件标识 */ public final static String INNER_LINK = "InnerLink"; /** * 校验返回结果码 */ public final static String UNIQUE = "0"; public final static String NOT_UNIQUE = "1"; /** * 用户名长度限制 */ public static final int USERNAME_MIN_LENGTH = 2; public static final int USERNAME_MAX_LENGTH = 20; /** * 密码长度限制 */ public static final int PASSWORD_MIN_LENGTH = 5; public static final int PASSWORD_MAX_LENGTH = 20; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-enums/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-enums 8 8 ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/BusinessStatus.java ================================================ package ginyi.common.enums; /** * 操作状态 * * @author ruoyi */ public enum BusinessStatus { /** * 成功 */ SUCCESS, /** * 失败 */ FAIL, } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/BusinessType.java ================================================ package ginyi.common.enums; /** * 业务操作类型 * * @author ruoyi */ public enum BusinessType { /** * 其它 */ OTHER, /** * 新增 */ INSERT, /** * 修改 */ UPDATE, /** * 删除 */ DELETE, /** * 授权 */ GRANT, /** * 导出 */ EXPORT, /** * 导入 */ IMPORT, /** * 强退 */ FORCE, /** * 生成代码 */ GENCODE, /** * 清空数据 */ CLEAN, } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/HttpMethod.java ================================================ package ginyi.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: ginyi-springboot/ginyi-common/ginyi-common-enums/src/main/java/ginyi/common/enums/OperatorType.java ================================================ package ginyi.common.enums; /** * 操作人类别 * * @author ruoyi */ public enum OperatorType { /** * 其它 */ OTHER, /** * 后台用户 */ MANAGE, /** * 手机端用户 */ MOBILE } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-exception/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-exception com.ginyi ginyi-common-result ${ginyi.version} org.springframework.boot spring-boot-starter-web com.ginyi ginyi-common-constants ${ginyi.version} ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/CommonException.java ================================================ package ginyi.common.exception; import ginyi.common.result.StateCode; import lombok.Data; /** * 通用的异常处理 */ @Data public class CommonException extends RuntimeException{ private StateCode state; private Object data; public CommonException(StateCode state, Object data) { this.state = state; this.data = data; } public CommonException(StateCode state) { this.state = state; this.data = data; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/UnPermissionException.java ================================================ package ginyi.common.exception; import ginyi.common.result.StateCode; import lombok.Data; /** * 没有对应的接口权限 */ @Data public class UnPermissionException extends RuntimeException{ private static final long serialVersionUID = 1L; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/UserPasswordNotMatchException.java ================================================ package ginyi.common.exception; /** * 用户密码不正确或不符合规范异常类 */ public class UserPasswordNotMatchException extends RuntimeException{ private static final long serialVersionUID = 1L; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/UserPasswordRetryLimitExceedException.java ================================================ package ginyi.common.exception; import ginyi.common.result.StateCode; import lombok.Data; /** * 用户错误最大次数异常类 */ @Data public class UserPasswordRetryLimitExceedException extends RuntimeException { private static final long serialVersionUID = 1L; private StateCode state; private Object data; public UserPasswordRetryLimitExceedException(StateCode state, Object data) { this.state = state; this.data = data; } public UserPasswordRetryLimitExceedException(StateCode state) { this.state = state; this.data = data; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-exception/src/main/java/ginyi/common/exception/handler/GlobalExceptionHandler.java ================================================ package ginyi.common.exception.handler; import com.alibaba.fastjson2.JSON; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.exception.UnPermissionException; import ginyi.common.exception.UserPasswordNotMatchException; import ginyi.common.exception.UserPasswordRetryLimitExceedException; import ginyi.common.result.CommonResult; import ginyi.common.result.StateCode; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MultipartException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * 全局异常处理类 */ @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { /** * 业务异常 ===>>> 处理数据异常处理 * * @param e * @return */ @ExceptionHandler(CommonException.class) @ResponseStatus(HttpStatus.OK) public CommonResult BusinessExceptionHandler(CommonException e) { if (e.getState() == StateCode.ERROR_SYSTEM) { return CommonResult.error(e.getState(), CommonMessageConstants.SYS_ERROR); } return CommonResult.error(e.getState(), e.getData()); } /** * 参数校验 ===> 方法参数错误异常 * * @param e * @return */ @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.OK) public CommonResult MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { List errorList = new ArrayList<>(); // 从异常对象中获取 ObjectError 对象 if (!e.getBindingResult().getAllErrors().isEmpty()) { for (ObjectError error : e.getBindingResult().getAllErrors()) { errorList.add(error.getDefaultMessage()); } } HashMap map = new HashMap<>(); map.put("errorMessageList", errorList); log.info(JSON.toJSONString(CommonResult.error(StateCode.ERROR_PARAMS, map))); return CommonResult.error(StateCode.ERROR_PARAMS, map); } /** * 参数校验 ===>>> 请求参数不合法 */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler(HttpMessageNotReadableException.class) public CommonResult HttpMessageNotReadableExceptionHandler(HttpMessageNotReadableException e) { return CommonResult.error(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } /** * 登录认证异常 ===> 用户名密码不匹配 */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler(UserPasswordNotMatchException.class) public CommonResult UserPasswordNotMatchExceptionHandler(UserPasswordNotMatchException e) { return CommonResult.error(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_PASSWORD_NOT_MATCH); } /** * 登录认证异常 ===> 用户登录失败次数超最大限制异常 */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler(UserPasswordRetryLimitExceedException.class) public CommonResult UserPasswordRetryLimitExceedExceptionHandler(UserPasswordRetryLimitExceedException e) { return CommonResult.error(e.getState(), e.getData()); } /** * 上传文件异常 * * @param e * @return */ @ExceptionHandler(MultipartException.class) @ResponseStatus(HttpStatus.OK) public CommonResult MultipartExceptionHandler(MultipartException e) { if (e instanceof MaxUploadSizeExceededException) { return CommonResult.error(StateCode.ERROR_MULTIPART, CommonMessageConstants.UPLOAD_SIZE_EXCEED); } return CommonResult.error(StateCode.ERROR_MULTIPART, CommonMessageConstants.UPLOAD_FILE_ERROR); } /** * 权限异常 ===>>>> 访问接口无权限 * * @param e * @return */ @ExceptionHandler({UnPermissionException.class}) @ResponseStatus(HttpStatus.OK) public CommonResult UnPermissionExceptionHandler(UnPermissionException e) { return CommonResult.error(StateCode.ERROR_NOT_PERMISSION, CommonMessageConstants.SYS_ERROR); } /** * 全局异常处理 ===> 处理其他所有未知异常 * * @param e * @return */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.OK) public CommonResult ExceptionHandler(Exception e) { log.error("系统异常", e); return CommonResult.error(StateCode.ERROR_SYSTEM, CommonMessageConstants.SYS_ERROR); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-mysql/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-mysql mysql mysql-connector-java com.alibaba druid-spring-boot-starter org.mybatis.spring.boot mybatis-spring-boot-starter com.baomidou mybatis-plus-boot-starter com.fasterxml.jackson.core jackson-databind com.ginyi ginyi-common-constants ${ginyi.version} com.ginyi ginyi-common-annotation ${ginyi.version} ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/MyPage.java ================================================ package ginyi.common.mysql; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.Data; /** * 分页参数有传,根据分页查询,没传,查询所有 */ @Data public class MyPage { /** * 当前页 */ private Long pageNum; /** * 每页条数 */ private Long pageSize; /** * 是否进行分页 */ private boolean isPage; public MyPage() { } public MyPage(Long page, Long pageSize) { if ((page != null && page > 0) && (pageSize != null && pageSize > 0)) { this.pageNum = page; this.pageSize = pageSize; this.isPage = true; }else { this.pageNum = 1L; this.pageSize = 10L; this.isPage = false; } } public Page getPage(){ return this.isPage ? new Page(this.pageNum, this.pageSize) : new Page().setSize(10000L); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/config/MyBatisPlusInterceptorConfig.java ================================================ package ginyi.common.mysql.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisPlusInterceptorConfig { @Bean public MybatisPlusInterceptor myBatisPlusInterceptor() { // 定义拦截器 MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页拦截器 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/handler/JacksonArrayTypeHandler.java ================================================ package ginyi.common.mysql.handler; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import java.util.List; @Slf4j public class JacksonArrayTypeHandler extends AbstractJsonTypeHandler { private final Class type; private final ObjectMapper objectMapper; public JacksonArrayTypeHandler(Class type) { objectMapper = new ObjectMapper(); this.type = type; } @SneakyThrows @Override protected List parse(String json) { if (type.equals(List.class)) { return objectMapper.readValue(json, List.class); } else { return JSON.parseArray(json, type); } } @SneakyThrows @Override protected String toJson(List obj) { return objectMapper.writeValueAsString(obj); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/handler/MyMetaObjectHandler.java ================================================ package ginyi.common.mysql.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import ginyi.common.constant.UserConstants; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Date; @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Resource private HttpServletRequest request; /** * 插入时的填充策略 * * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createBy", request.getAttribute(UserConstants.CURRENT_USER), metaObject); this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateBy", request.getAttribute(UserConstants.CURRENT_USER), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } /** * 更新时的填充策略 * * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateBy", request.getAttribute(UserConstants.CURRENT_USER), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-mysql/src/main/java/ginyi/common/mysql/interceptor/MyInterceptor.java ================================================ package ginyi.common.mysql.interceptor; import ginyi.common.annotation.CreateBy; import ginyi.common.annotation.CreateTime; import ginyi.common.annotation.UpdateBy; import ginyi.common.annotation.UpdateTime; import ginyi.common.constant.UserConstants; import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; 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 org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.util.Date; import java.util.Set; /** * 自定义 Mybatis 插件,自动设置 createTime、createBy 和 updateTime、updateBy 的值。 * 拦截 update 操作(添加和修改) */ @Component @Intercepts({@Signature( type = Executor.class, method = "update", args = {MappedStatement.class, Object.class} )}) public class MyInterceptor implements Interceptor { @Resource private HttpServletRequest request; @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; // 获取 SQL 命令 SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); //只判断新增和修改 if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { // 获取参数 Object parameter = invocation.getArgs()[1]; //批量操作时 if (parameter instanceof MapperMethod.ParamMap) { MapperMethod.ParamMap map = (MapperMethod.ParamMap) parameter; Set keys = map.keySet(); for (Object key : keys) { if (map.get(key) != null) { setParameter(map.get(key), sqlCommandType); } } } else { setParameter(parameter, sqlCommandType); } } return invocation.proceed(); } public void setParameter(Object parameter, SqlCommandType sqlCommandType) throws Throwable { Class aClass = parameter.getClass(); Field[] declaredFields = aClass.getSuperclass().getDeclaredFields(); for (Field field : declaredFields) { if (SqlCommandType.INSERT.equals(sqlCommandType)) { // insert 语句插入 createBy if (field.getAnnotation(CreateBy.class) != null) { field.setAccessible(true); field.set(parameter, request.getAttribute(UserConstants.CURRENT_USER)); } // insert 语句插入 createTime if (field.getAnnotation(CreateTime.class) != null) { field.setAccessible(true); field.set(parameter, new Date()); } } if (SqlCommandType.UPDATE.equals(sqlCommandType)) { // update 语句插入 updateBy if (field.getAnnotation(UpdateBy.class) != null) { field.setAccessible(true); field.set(parameter, request.getAttribute(UserConstants.CURRENT_USER)); } // update 语句插入 updateTime if (field.getAnnotation(UpdateTime.class) != null) { field.setAccessible(true); field.set(parameter, new Date()); } } } } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-redis/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-redis org.springframework.data spring-data-redis redis.clients jedis org.apache.commons commons-pool2 com.alibaba.fastjson2 fastjson2 ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-redis/src/main/java/ginyi/common/redis/cache/RedisCache.java ================================================ package ginyi.common.redis.cache; import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.DataType; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.TimeUnit; /** * spring redis 工具类 * * @author ruoyi **/ @SuppressWarnings(value = {"unchecked", "rawtypes"}) @Component public class RedisCache { @Autowired public RedisTemplate redisTemplate; /** * 缓存基本的对象,Integer、String、实体类等,时间为30分钟 * * @param key 缓存的键值 * @param value 缓存的值 */ public void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 */ public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, value, timeout, timeUnit); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout) { return expire(key, timeout, TimeUnit.SECONDS); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @param unit 时间单位 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout, final TimeUnit unit) { return redisTemplate.expire(key, timeout, unit); } /** * 获取有效时间 * * @param key Redis键 * @return 有效时间 */ public long getExpire(final String key) { return redisTemplate.getExpire(key); } /** * 判断 key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } /** * 获取数据类型 * @param key * @return */ public DataType getType(String key){ return redisTemplate.type(key); } /** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */ public T getCacheObject(final String key, Class clazz) { Object o = redisTemplate.opsForValue().get(key); if (o == null) { return null; } return JSON.parseObject(JSON.toJSONString(o), clazz); } /** * 删除单个对象 * * @param key */ public boolean removeCacheObject(final String key) { Set keys = redisTemplate.keys(key); return redisTemplate.delete(keys) > 0; } /** * 删除集合对象 * * @param collection 多个对象 * @return */ public boolean removeCacheObject(final Collection collection) { return redisTemplate.delete(collection) > 0; } /** * 获取指定前缀的所有key * * @param key */ public Set getKeys(final String key) { return redisTemplate.keys(key); } /** * 获取数据 * @param key * @return */ public Object getValue(String key){ return redisTemplate.opsForValue().get(key); } /** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public long setCacheList(final String key, final List dataList) { Long count = redisTemplate.opsForList().rightPushAll(key, dataList); return count == null ? 0 : count; } /** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */ public List getCacheList(final String key, Class clazz) { ArrayList resultList = new ArrayList<>(); List list = redisTemplate.opsForList().range(key, 0, -1); for (Object o : list) { T t = JSON.parseObject(JSON.toJSONString(o), clazz); resultList.add(t); } return resultList; } /** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ public BoundSetOperations setCacheSet(final String key, final Set dataSet) { BoundSetOperations setOperation = redisTemplate.boundSetOps(key); Iterator it = dataSet.iterator(); while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; } /** * 获得缓存的set * * @param key * @return */ public Set getCacheSet(final String key) { return redisTemplate.opsForSet().members(key); } /** * 缓存Map * * @param key * @param dataMap */ public void setCacheMap(final String key, final Map dataMap) { if (dataMap != null) { redisTemplate.opsForHash().putAll(key, dataMap); } } /** * 获得缓存的Map * * @param key * @return */ public Map getCacheMap(final String key) { return redisTemplate.opsForHash().entries(key); } /** * 往Hash中存入数据 * * @param key Redis键 * @param hKey Hash键 * @param value 值 */ public void setCacheMapValue(final String key, final String hKey, final T value) { redisTemplate.opsForHash().put(key, hKey, value); } /** * 获取Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */ public T getCacheMapValue(final String key, final String hKey) { HashOperations opsForHash = redisTemplate.opsForHash(); return opsForHash.get(key, hKey); } /** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ public List getMultiCacheMapValue(final String key, final Collection hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } /** * 删除Hash中的某条数据 * * @param key Redis键 * @param hKey Hash键 * @return 是否成功 */ public boolean deleteCacheMapValue(final String key, final String hKey) { return redisTemplate.opsForHash().delete(key, hKey) > 0; } /** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */ public Collection keys(final String pattern) { return redisTemplate.keys(pattern); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-redis/src/main/java/ginyi/common/redis/config/RedisConfig.java ================================================ package ginyi.common.redis.config; import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) public class RedisConfig extends CachingConfigurerSupport { @Bean @SuppressWarnings(value = {"unchecked", "rawtypes"}) public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate template = new RedisTemplate<>(); //使用fastjson序列化 FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer); // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setConnectionFactory(connectionFactory); return template; } @Bean public DefaultRedisScript limitScript() { DefaultRedisScript redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(limitScriptText()); redisScript.setResultType(Long.class); return redisScript; } /** * 限流脚本 */ private String limitScriptText() { return "local key = KEYS[1]\n" + "local count = tonumber(ARGV[1])\n" + "local time = tonumber(ARGV[2])\n" + "local current = redis.call('get', key);\n" + "if current and tonumber(current) > count then\n" + " return tonumber(current);\n" + "end\n" + "current = redis.call('incr', key)\n" + "if tonumber(current) == 1 then\n" + " redis.call('expire', key, time)\n" + "end\n" + "return tonumber(current);"; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-result/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-result io.springfox springfox-swagger2 ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-result/src/main/java/ginyi/common/result/CommonResult.java ================================================ package ginyi.common.result; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; @Data @ApiModel(value = "结果类") public class CommonResult implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "状态码") private Integer code; @ApiModelProperty(value = "状态信息") private String msg; @ApiModelProperty(value = "返回数据") private T data; /** * 返回格式 * @param state * @param data * @param * @return */ private static CommonResult res(StateCode state, T data){ CommonResult commonResult = new CommonResult<>(); commonResult.setCode(state.getCode()); commonResult.setMsg(state.getMessage()); commonResult.setData(data); return commonResult; } /** * 成功的返回,不需要 data * @param * @return */ public static CommonResult success(){ return res(StateCode.SUCCESS, null); } /** * 成功的返回,需要 data * @param data * @param * @return */ public static CommonResult success(T data){ return res(StateCode.SUCCESS, data); } /** * 失败的返回,不需要 data * @param state * @return */ public static CommonResult error(StateCode state){ return res(state, null); } /** * 失败的返回,需要 data, 即错误信息 * @param state * @param * @return */ public static CommonResult error(StateCode state, T data){ return res(state, data); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-result/src/main/java/ginyi/common/result/StateCode.java ================================================ package ginyi.common.result; public enum StateCode { SUCCESS(200, "操作成功"), ERROR_SYSTEM(5000, "系统异常"), ERROR_UNAUTHENTICATION(5001, "登陆授权异常"), ERROR_TIMEOUT_REQUEST(5002, "请求超时"), ERROR_TIMEOUT_TOKEN(5003, "无效令牌"), ERROR_INVALID_SIGN(5004, "无效签名"), ERROR_AUTHENTICATION_VALID(5005, "认证失败或令牌已过期"), ERROR_NOT_PERMISSION(5006, "权限不足"), ERROR_LIMITED(5007, "操作受限"), ERROR_BUSINESS(6000,"业务逻辑异常"), ERROR_PARAMS(6001,"参数校验错误"), ERROR_REQUEST_PARAMS(6002,"请求参数异常"), ERROR_PARAMS_SERVICE(6003,"参数逻辑校验错误"), ERROR_DATA_FORMAT(6004, "数据格式错误"), ERROR_EXIST(6005, "数据已存在"), ERROR_NOT_EXIST(6006, "数据不存在"), ERROR_NOT_SUPPORT(7000, "不支持该文件类型"), ERROR_LIMIT_EXCEEDED(7001, "超出请求次数上限"), ERROR_MULTIPART(7002, "文件上传异常"), ; private final int code; private final String message; StateCode(final int code, final String message) { this.code = code; this.message = message; } public int getCode(){ return this.code; } public String getMessage() { return message; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-swagger/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-swagger io.springfox springfox-swagger2 io.springfox springfox-swagger-ui com.github.xiaoymin knife4j-spring-boot-starter ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-swagger/src/main/java/ginyi/common/swagger/AddGroup.java ================================================ package ginyi.common.swagger; /** * 此接口不要删除,用于参数校验的分组 */ public interface AddGroup { } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-swagger/src/main/java/ginyi/common/swagger/Swagger2Config.java ================================================ package ginyi.common.swagger; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import static springfox.documentation.builders.RequestHandlerSelectors.basePackage; public class Swagger2Config { /** * 标题 */ @Value("${swagger.swagger-title}") private String title; /** * 分组 */ @Value("${swagger.group-name}") private String groupName; /** * 描述 */ @Value("${swagger.swagger-description}") private String description; /** * URL */ @Value("${swagger.swagger-url}") private String url; /** * 作者 */ @Value("${swagger.swagger-contact.name}") private String contactName; /** * 作者邮箱 */ @Value("${swagger.swagger-contact.email}") private String contactEmail; /** * 版本 */ @Value("${swagger.swagger-version}") private String version; @Bean public Docket createAdminApiDoc() { return new Docket(DocumentationType.SWAGGER_2) .groupName(groupName) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.any()) .build(); } // 构建 api 文档的详细信息函数 private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(title) .description(description) .termsOfServiceUrl(url) .contact(new Contact(contactName, null, contactEmail)) .version(version) .build(); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-swagger/src/main/java/ginyi/common/swagger/UpdateGroup.java ================================================ package ginyi.common.swagger; /** * 此接口不要删除,用于参数校验的分组 */ public interface UpdateGroup { } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/pom.xml ================================================ ginyi-common com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common-utils 8 8 org.springframework.boot spring-boot-starter-web org.apache.commons commons-lang3 io.jsonwebtoken jjwt ${jwt.version} com.alibaba.fastjson2 fastjson2 com.sun.mail javax.mail ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/Constants.java ================================================ package ginyi.common.utils; /** * 通用常量信息 * * @author ruoyi */ public class Constants { /** * UTF-8 字符集 */ public static final String UTF8 = "UTF-8"; /** * GBK 字符集 */ public static final String GBK = "GBK"; /** * 通用成功标识 */ public static final String SUCCESS = "0"; /** * 通用失败标识 */ public static final String FAIL = "1"; /** * 登录成功 */ public static final String LOGIN_SUCCESS = "Success"; /** * 注销 */ public static final String LOGOUT = "Logout"; /** * 注册 */ public static final String REGISTER = "Register"; /** * 登录失败 */ public static final String LOGIN_FAIL = "Error"; /** * 令牌前缀 */ public static final String TOKEN_PREFIX = "Bearer "; /** * 令牌前缀 */ public static final String LOGIN_USER_KEY = "login_user_key"; /** * 资源映射路径 前缀 */ public static final String RESOURCE_PREFIX = "/profile"; /** * 令牌 */ public static final String TOKEN = "token"; /** * 验证码有效期(分钟) */ public static final Integer CAPTCHA_EXPIRATION = 2; } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/DateUtils.java ================================================ package ginyi.common.utils; 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.Date; /** * 时间工具类 * * @author ruoyi */ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { public static String YYYY = "yyyy"; public static String YYYY_MM = "yyyy-MM"; public static String YYYY_MM_DD = "yyyy-MM-dd"; public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; private static String[] parsePatterns = { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; /** * 获取当前Date型日期 * * @return Date() 当前日期 */ public static Date getNowDate() { return new Date(); } /** * 获取当前日期, 默认格式为yyyy-MM-dd * * @return String */ public static String getDate() { return dateTimeNow(YYYY_MM_DD); } public static final String getTime() { return dateTimeNow(YYYY_MM_DD_HH_MM_SS); } public static final String dateTimeNow() { return dateTimeNow(YYYYMMDDHHMMSS); } public static final String dateTimeNow(final String format) { return parseDateToStr(format, new Date()); } public static final String dateTime(final Date date) { return parseDateToStr(YYYY_MM_DD, date); } public static final String parseDateToStr(final String format, final Date date) { return new SimpleDateFormat(format).format(date); } public static final Date dateTime(final String format, final String ts) { try { return new SimpleDateFormat(format).parse(ts); } catch (ParseException e) { throw new RuntimeException(e); } } /** * 日期路径 即年/月/日 如2018/08/08 */ public static final String datePath() { Date now = new Date(); return DateFormatUtils.format(now, "yyyy/MM/dd"); } /** * 日期路径 即年/月/日 如20180808 */ public static final String dateTime() { Date now = new Date(); return DateFormatUtils.format(now, "yyyyMMdd"); } /** * 日期型字符串转化为日期 格式 */ public static Date parseDate(Object str) { if (str == null) { return null; } try { return parseDate(str.toString(), parsePatterns); } catch (ParseException e) { return null; } } /** * 获取服务器启动时间 */ public static Date getServerStartDate() { long time = ManagementFactory.getRuntimeMXBean().getStartTime(); return new Date(time); } /** * 计算相差天数 */ public static int differentDaysByMillisecond(Date date1, Date date2) { return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); } /** * 计算两个时间差 */ 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()); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/EmailUtils.java ================================================ package ginyi.common.utils; import cn.hutool.extra.mail.MailUtil; /** * 邮箱工具类 * 如何使用: * 1、需要在对应的应用服务模块下(如ginyi-server-admin)的resource目录下,新建config目录,再新建mail.setting配置文件 * 2、具体配置信息参考 https://www.hutool.cn/docs/#/extra/邮件工具-MailUtil */ public class EmailUtils { /** * 单点发送 * @param to 目标 * @param subject 标题 * @param message 信息 * @param isHtml 是否为html信息 * @return */ public static boolean sendToOne(String to, String subject, String message, Boolean isHtml) { String result = MailUtil.send(to, subject, message, isHtml); return result != null; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/LogUtils.java ================================================ package ginyi.common.utils; /** * 处理并记录日志文件 * * @author ruoyi */ public class LogUtils { public static String getBlock(Object msg) { if (msg == null) { msg = ""; } return "[" + msg.toString() + "]"; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/LunarCalendarUtil.java ================================================ package ginyi.common.utils; import java.util.Date; import java.util.GregorianCalendar; /** * 工具类,实现阴阳历互转 */ public class LunarCalendarUtil { /** * 支持转换的最小农历年份 */ public static final int MIN_YEAR = 1900; /** * 支持转换的最大农历年份 */ public static final int MAX_YEAR = 2099; /** * 公历每月前的天数 */ private static final int[] DAYS_BEFORE_MONTH = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; /** * 用来表示1900年到2099年间农历年份的相关信息,共24位bit的16进制表示,其中: * 1. 前4位表示该年闰哪个月; * 2. 5-17位表示农历年份13个月的大小月分布,0表示小,1表示大; * 3. 最后7位表示农历年首(正月初一)对应的公历日期。 * 以2014年的数据0x955ABF为例说明: * 1001 0101 0101 1010 1011 1111 * 闰九月 农历正月初一对应公历1月31号 */ private static final int[] LUNAR_INFO = { /*1900*/ 0x84B6BF, /*1901-1910*/ 0x04AE53, 0x0A5748, 0x5526BD, 0x0D2650, 0x0D9544, 0x46AAB9, 0x056A4D, 0x09AD42, 0x24AEB6, 0x04AE4A, /*1911-1920*/ 0x6A4DBE, 0x0A4D52, 0x0D2546, 0x5D52BA, 0x0B544E, 0x0D6A43, 0x296D37, 0x095B4B, 0x749BC1, 0x049754, /*1921-1930*/ 0x0A4B48, 0x5B25BC, 0x06A550, 0x06D445, 0x4ADAB8, 0x02B64D, 0x095742, 0x2497B7, 0x04974A, 0x664B3E, /*1931-1940*/ 0x0D4A51, 0x0EA546, 0x56D4BA, 0x05AD4E, 0x02B644, 0x393738, 0x092E4B, 0x7C96BF, 0x0C9553, 0x0D4A48, /*1941-1950*/ 0x6DA53B, 0x0B554F, 0x056A45, 0x4AADB9, 0x025D4D, 0x092D42, 0x2C95B6, 0x0A954A, 0x7B4ABD, 0x06CA51, /*1951-1960*/ 0x0B5546, 0x555ABB, 0x04DA4E, 0x0A5B43, 0x352BB8, 0x052B4C, 0x8A953F, 0x0E9552, 0x06AA48, 0x6AD53C, /*1961-1970*/ 0x0AB54F, 0x04B645, 0x4A5739, 0x0A574D, 0x052642, 0x3E9335, 0x0D9549, 0x75AABE, 0x056A51, 0x096D46, /*1971-1980*/ 0x54AEBB, 0x04AD4F, 0x0A4D43, 0x4D26B7, 0x0D254B, 0x8D52BF, 0x0B5452, 0x0B6A47, 0x696D3C, 0x095B50, /*1981-1990*/ 0x049B45, 0x4A4BB9, 0x0A4B4D, 0xAB25C2, 0x06A554, 0x06D449, 0x6ADA3D, 0x0AB651, 0x095746, 0x5497BB, /*1991-2000*/ 0x04974F, 0x064B44, 0x36A537, 0x0EA54A, 0x86B2BF, 0x05AC53, 0x0AB647, 0x5936BC, 0x092E50, 0x0C9645, /*2001-2010*/ 0x4D4AB8, 0x0D4A4C, 0x0DA541, 0x25AAB6, 0x056A49, 0x7AADBD, 0x025D52, 0x092D47, 0x5C95BA, 0x0A954E, /*2011-2020*/ 0x0B4A43, 0x4B5537, 0x0AD54A, 0x955ABF, 0x04BA53, 0x0A5B48, 0x652BBC, 0x052B50, 0x0A9345, 0x474AB9, /*2021-2030*/ 0x06AA4C, 0x0AD541, 0x24DAB6, 0x04B64A, 0x6a573D, 0x0A4E51, 0x0D2646, 0x5E933A, 0x0D534D, 0x05AA43, /*2031-2040*/ 0x36B537, 0x096D4B, 0xB4AEBF, 0x04AD53, 0x0A4D48, 0x6D25BC, 0x0D254F, 0x0D5244, 0x5DAA38, 0x0B5A4C, /*2041-2050*/ 0x056D41, 0x24ADB6, 0x049B4A, 0x7A4BBE, 0x0A4B51, 0x0AA546, 0x5B52BA, 0x06D24E, 0x0ADA42, 0x355B37, /*2051-2060*/ 0x09374B, 0x8497C1, 0x049753, 0x064B48, 0x66A53C, 0x0EA54F, 0x06AA44, 0x4AB638, 0x0AAE4C, 0x092E42, /*2061-2070*/ 0x3C9735, 0x0C9649, 0x7D4ABD, 0x0D4A51, 0x0DA545, 0x55AABA, 0x056A4E, 0x0A6D43, 0x452EB7, 0x052D4B, /*2071-2080*/ 0x8A95BF, 0x0A9553, 0x0B4A47, 0x6B553B, 0x0AD54F, 0x055A45, 0x4A5D38, 0x0A5B4C, 0x052B42, 0x3A93B6, /*2081-2090*/ 0x069349, 0x7729BD, 0x06AA51, 0x0AD546, 0x54DABA, 0x04B64E, 0x0A5743, 0x452738, 0x0D264A, 0x8E933E, /*2091-2099*/ 0x0D5252, 0x0DAA47, 0x66B53B, 0x056D4F, 0x04AE45, 0x4A4EB9, 0x0A4D4C, 0x0D1541, 0x2D92B5 }; /** * 将农历日期转换为公历日期 * * @param year 农历年份 * @param month 农历月 * @param monthDay 农历日 * @param isLeapMonth 该月是否是闰月(该参数可以根据本类中leapMonth()方法,先判断一下要查询的年份是否有闰月,并且闰的几月) * @return 返回农历日期对应的公历日期,year0, month1, day2. */ public static final int[] lunarToSolar(int year, int month, int monthDay, boolean isLeapMonth) { int dayOffset; int leapMonth; int i; if (year < MIN_YEAR || year > MAX_YEAR || month < 1 || month > 12 || monthDay < 1 || monthDay > 30) { throw new IllegalArgumentException( "Illegal lunar date, must be like that:\n\t" + "year : 1900~2099\n\t" + "month : 1~12\n\t" + "day : 1~30"); } dayOffset = (LUNAR_INFO[year - MIN_YEAR] & 0x001F) - 1; if (((LUNAR_INFO[year - MIN_YEAR] & 0x0060) >> 5) == 2) { dayOffset += 31; } for (i = 1; i < month; i++) { if ((LUNAR_INFO[year - MIN_YEAR] & (0x80000 >> (i - 1))) == 0) { dayOffset += 29; } else { dayOffset += 30; } } dayOffset += monthDay; leapMonth = (LUNAR_INFO[year - MIN_YEAR] & 0xf00000) >> 20; // 这一年有闰月 if (leapMonth != 0) { if (month > leapMonth || (month == leapMonth && isLeapMonth)) { if ((LUNAR_INFO[year - MIN_YEAR] & (0x80000 >> (month - 1))) == 0) { dayOffset += 29; } else { dayOffset += 30; } } } if (dayOffset > 366 || (year % 4 != 0 && dayOffset > 365)) { year += 1; if (year % 4 == 1) { dayOffset -= 366; } else { dayOffset -= 365; } } int[] solarInfo = new int[3]; for (i = 1; i < 13; i++) { int iPos = DAYS_BEFORE_MONTH[i]; if (year % 4 == 0 && i > 2) { iPos += 1; } if (year % 4 == 0 && i == 2 && iPos + 1 == dayOffset) { solarInfo[1] = i; solarInfo[2] = dayOffset - 31; break; } if (iPos >= dayOffset) { solarInfo[1] = i; iPos = DAYS_BEFORE_MONTH[i - 1]; if (year % 4 == 0 && i > 2) { iPos += 1; } if (dayOffset > iPos) { solarInfo[2] = dayOffset - iPos; } else if (dayOffset == iPos) { if (year % 4 == 0 && i == 2) { solarInfo[2] = DAYS_BEFORE_MONTH[i] - DAYS_BEFORE_MONTH[i - 1] + 1; } else { solarInfo[2] = DAYS_BEFORE_MONTH[i] - DAYS_BEFORE_MONTH[i - 1]; } } else { solarInfo[2] = dayOffset; } break; } } solarInfo[0] = year; return solarInfo; } /** * 将公历日期转换为农历日期,且标识是否是闰月 * (如果当年有闰月,被闰的阴历月份所对应的阳历日期计算出来不对,后期自己根据本方法leap3 返回值判断如果为1,给月份+1,如果为0就是正确的,不用在操作,阴历闰月对应的阳历日期计算出来的阴历闰月日期正确,leap3为0) * * @param year * @param month 传入需要为单数如 08 为8 * @param monthDay * @return 返回公历日期对应的农历日期,year0,month1,day2,leap3(0 为计算正常 1 月份会比正确少一个月,需要月份+1) */ public static final int[] solarToLunar(int year, int month, int monthDay) { int[] lunarDate = new int[4]; Date baseDate = new GregorianCalendar(1900, 0, 31).getTime(); Date objDate = new GregorianCalendar(year, month - 1, monthDay).getTime(); int offset = (int) ((objDate.getTime() - baseDate.getTime()) / 86400000L); // 用offset减去每农历年的天数计算当天是农历第几天 // iYear最终结果是农历的年份, offset是当年的第几天 int iYear, daysOfYear = 0; for (iYear = MIN_YEAR; iYear <= MAX_YEAR && offset > 0; iYear++) { daysOfYear = daysInLunarYear(iYear); offset -= daysOfYear; } if (offset < 0) { offset += daysOfYear; iYear--; } // 农历年份 lunarDate[0] = iYear; int leapMonth = leapMonth(iYear); // 闰哪个月,1-12 boolean isLeap = false; // 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天 int iMonth, daysOfMonth = 0; for (iMonth = 1; iMonth <= 13 && offset > 0; iMonth++) { daysOfMonth = daysInLunarMonth(iYear, iMonth); offset -= daysOfMonth; } // 当前月超过闰月,要校正 if (leapMonth != 0 && iMonth > leapMonth) { --iMonth; if (iMonth == leapMonth) { isLeap = true; } } // offset小于0时,也要校正 if (offset < 0) { offset += daysOfMonth; --iMonth; } lunarDate[1] = iMonth; lunarDate[2] = offset + 1; lunarDate[3] = isLeap ? 1 : 0; return lunarDate; } /** * 传回农历year年month月的总天数 * * @param year 要计算的年份 * @param month 要计算的月 * @return 传回天数 */ final public static int daysInMonth(int year, int month) { return daysInMonth(year, month, false); } /** * 传回农历year年month月的总天数 * * @param year 要计算的年份 * @param month 要计算的月 * @param leap 当月是否是闰月 * @return 传回天数,如果闰月是错误的,返回0. */ public static final int daysInMonth(int year, int month, boolean leap) { int leapMonth = leapMonth(year); int offset = 0; // 如果本年有闰月且month大于闰月时,需要校正 if (leapMonth != 0 && month > leapMonth) { offset = 1; } // 不考虑闰月 if (!leap) { return daysInLunarMonth(year, month + offset); } else { // 传入的闰月是正确的月份 if (leapMonth != 0 && leapMonth == month) { return daysInLunarMonth(year, month + 1); } } return 0; } /** * 传回农历 year年的总天数 * * @param year 将要计算的年份 * @return 返回传入年份的总天数 */ private static int daysInLunarYear(int year) { int i, sum = 348; if (leapMonth(year) != 0) { sum = 377; } int monthInfo = LUNAR_INFO[year - MIN_YEAR] & 0x0FFF80; for (i = 0x80000; i > 0x7; i >>= 1) { if ((monthInfo & i) != 0) { sum += 1; } } return sum; } /** * 传回农历 year年month月的总天数,总共有13个月包括闰月 * * @param year 将要计算的年份 * @param month 将要计算的月份 * @return 传回农历 year年month月的总天数 */ public static int daysInLunarMonth(int year, int month) { if ((LUNAR_INFO[year - MIN_YEAR] & (0x100000 >> month)) == 0) { return 29; } else { return 30; } } /** * 传回农历 year年闰哪个月 1-12 , 没闰传回 0 * * @param year 将要计算的年份 * @return 传回农历 year年闰哪个月1-12, 没闰传回 0 */ public static int leapMonth(int year) { return ((LUNAR_INFO[year - MIN_YEAR] & 0xF00000)) >> 20; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/MessageUtils.java ================================================ package ginyi.common.utils; import ginyi.common.utils.spring.SpringUtils; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; /** * 获取i18n资源文件 * * @author ruoyi */ public class MessageUtils { /** * 根据消息键和参数 获取消息 委托给spring messageSource * * @param code 消息键 * @param args 参数 * @return 获取国际化翻译值 */ public static String message(String code, Object... args) { MessageSource messageSource = SpringUtils.getBean(MessageSource.class); return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/NumberUtils.java ================================================ package ginyi.common.utils; import java.math.BigDecimal; import java.math.RoundingMode; public class NumberUtils { public static double round(double number, int decimalPlaces) { BigDecimal bigDecimal = new BigDecimal(Double.toString(number)); bigDecimal = bigDecimal.setScale(decimalPlaces, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/ServletUtils.java ================================================ package ginyi.common.utils; import ginyi.common.utils.text.Convert; 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.util.Collections; import java.util.HashMap; import java.util.Map; /** * 客户端工具类 * * @author ruoyi */ public class ServletUtils { /** * 获取String参数 */ public static String getParameter(String name) { return getRequest().getParameter(name); } /** * 获取String参数 */ public static String getParameter(String name, String defaultValue) { return Convert.toStr(getRequest().getParameter(name), defaultValue); } /** * 获取Integer参数 */ public static Integer getParameterToInt(String name) { return Convert.toInt(getRequest().getParameter(name)); } /** * 获取Integer参数 */ public static Integer getParameterToInt(String name, Integer defaultValue) { return Convert.toInt(getRequest().getParameter(name), defaultValue); } /** * 获取Boolean参数 */ public static Boolean getParameterToBool(String name) { return Convert.toBool(getRequest().getParameter(name)); } /** * 获取Boolean参数 */ public static Boolean getParameterToBool(String name, Boolean defaultValue) { return Convert.toBool(getRequest().getParameter(name), defaultValue); } /** * 获得所有请求参数 * * @param request 请求对象{@link ServletRequest} * @return Map */ public static Map 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(), ",")); } return params; } /** * 获取request */ public static HttpServletRequest getRequest() { return getRequestAttributes().getRequest(); } /** * 获取response */ public static HttpServletResponse getResponse() { return getRequestAttributes().getResponse(); } /** * 获取session */ public static HttpSession getSession() { return getRequest().getSession(); } public static ServletRequestAttributes getRequestAttributes() { RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); return (ServletRequestAttributes) attributes; } /** * 将字符串渲染到客户端 * * @param response 渲染对象 * @param string 待渲染的字符串 */ public static void renderString(HttpServletResponse response, String string) { try { response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().print(string); } catch (IOException e) { e.printStackTrace(); } } /** * 是否是Ajax异步请求 * * @param request */ public static boolean isAjaxRequest(HttpServletRequest request) { String accept = request.getHeader("accept"); if (accept != null && accept.contains("application/json")) { return true; } String xRequestedWith = request.getHeader("X-Requested-With"); if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { return true; } String uri = request.getRequestURI(); if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { return true; } String ajax = request.getParameter("__ajax"); return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); } /** * 内容编码 * * @param str 内容 * @return 编码后的内容 */ public static String urlEncode(String str) { try { return URLEncoder.encode(str, Constants.UTF8); } catch (UnsupportedEncodingException e) { return StringUtils.EMPTY; } } /** * 内容解码 * * @param str 内容 * @return 解码后的内容 */ public static String urlDecode(String str) { try { return URLDecoder.decode(str, Constants.UTF8); } catch (UnsupportedEncodingException e) { return StringUtils.EMPTY; } } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/StringUtils.java ================================================ package ginyi.common.utils; import ginyi.common.utils.text.StrFormatter; /** * 字符串工具类 * * @author ruoyi */ public class StringUtils extends org.apache.commons.lang3.StringUtils { /** * 空字符串 */ private static final String NULLSTR = ""; /** * * 判断一个字符串是否为空串 * * @param str String * @return true:为空 false:非空 */ public static boolean isEmpty(String str) { return isNull(str) || NULLSTR.equals(str.trim()); } /** * * 判断一个对象是否为空 * * @param object Object * @return true:为空 false:非空 */ public static boolean isNull(Object object) { return object == null; } /** * * 判断一个对象是否非空 * * @param object Object * @return true:非空 false:空 */ public static boolean isNotNull(Object object) { return !isNull(object); } /** * 获取参数不为空值 * * @param value defaultValue 要判断的value * @return value 返回值 */ public static T nvl(T value, T defaultValue) { return value != null ? value : defaultValue; } /** * 是否包含字符串 * * @param str 验证字符串 * @param strs 字符串组 * @return 包含返回true */ public static boolean inStringIgnoreCase(String str, String... strs) { if (str != null && strs != null) { for (String s : strs) { if (str.equalsIgnoreCase(trim(s))) { return true; } } } return false; } /** * 去空格 */ public static String trim(String str) { return (str == null ? "" : str.trim()); } /** * 格式化文本, {} 表示占位符
* 此方法只是简单将占位符 {} 按照顺序替换为参数
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
* 例:
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
* * @param template 文本模板,被替换的部分用 {} 表示 * @param params 参数值 * @return 格式化后的文本 */ public static String format(String template, Object... params) { if (isEmpty(params) || isEmpty(template)) { return template; } return StrFormatter.format(template, params); } /** * * 判断一个对象数组是否为空 * * @param objects 要判断的对象数组 * * @return true:为空 false:非空 */ public static boolean isEmpty(Object[] objects) { return isNull(objects) || (objects.length == 0); } /** * * 判断一个对象数组是否非空 * * @param objects 要判断的对象数组 * @return true:非空 false:空 */ public static boolean isNotEmpty(Object[] objects) { return !isEmpty(objects); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/Threads.java ================================================ package ginyi.common.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; /** * 线程相关工具类. * * @author ruoyi */ public class Threads { private static final Logger logger = LoggerFactory.getLogger(Threads.class); /** * sleep等待,单位为毫秒 */ public static void sleep(long milliseconds) { try { Thread.sleep(milliseconds); } catch (InterruptedException e) { return; } } /** * 停止线程池 * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. * 如果仍然超時,則強制退出. * 另对在shutdown时线程本身被调用中断做了处理. */ public static void shutdownAndAwaitTermination(ExecutorService pool) { if (pool != null && !pool.isShutdown()) { pool.shutdown(); try { if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { pool.shutdownNow(); if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { logger.info("Pool did not terminate"); } } } catch (InterruptedException ie) { pool.shutdownNow(); Thread.currentThread().interrupt(); } } } /** * 打印线程异常信息 */ public static void printException(Runnable r, Throwable t) { if (t == null && r instanceof Future) { try { Future future = (Future) r; if (future.isDone()) { future.get(); } } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } if (t != null) { logger.error(t.getMessage(), t); } } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/enums/UserStatus.java ================================================ package ginyi.common.utils.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: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/http/HttpHelper.java ================================================ package ginyi.common.utils.http; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; /** * 通用http工具封装 * * @author ruoyi */ public class HttpHelper { private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); public static String getBodyString(ServletRequest request) { StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try (InputStream inputStream = request.getInputStream()) { reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { LOGGER.warn("getBodyString出现问题!"); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { LOGGER.error(ExceptionUtils.getMessage(e)); } } } return sb.toString(); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/http/HttpUtils.java ================================================ package ginyi.common.utils.http; import ginyi.common.utils.Constants; import ginyi.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.*; import java.io.*; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; import java.security.cert.X509Certificate; /** * 通用http发送方法 * * @author ruoyi */ public class HttpUtils { private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); /** * 向指定 URL 发送GET方法的请求 * * @param url 发送请求的 URL * @return 所代表远程资源的响应结果 */ public static String sendGet(String url) { return sendGet(url, StringUtils.EMPTY); } /** * 向指定 URL 发送GET方法的请求 * * @param url 发送请求的 URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return 所代表远程资源的响应结果 */ public static String sendGet(String url, String param) { return sendGet(url, param, Constants.UTF8); } /** * 向指定 URL 发送GET方法的请求 * * @param url 发送请求的 URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @param contentType 编码类型 * @return 所代表远程资源的响应结果 */ public static String sendGet(String url, String param, String contentType) { StringBuilder result = new StringBuilder(); BufferedReader in = null; try { String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; log.info("sendGet - {}", urlNameString); URL realUrl = new URL(urlNameString); URLConnection connection = realUrl.openConnection(); connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); connection.connect(); in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); String line; while ((line = in.readLine()) != null) { result.append(line); } log.info("recv - {}", result); } catch (ConnectException e) { log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); } catch (SocketTimeoutException e) { log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); } catch (IOException e) { log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); } catch (Exception e) { log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); } finally { try { if (in != null) { in.close(); } } catch (Exception ex) { log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); } } return result.toString(); } /** * 向指定 URL 发送POST方法的请求 * * @param url 发送请求的 URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; StringBuilder result = new StringBuilder(); try { log.info("sendPost - {}", url); URL realUrl = new URL(url); URLConnection conn = realUrl.openConnection(); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); conn.setRequestProperty("Accept-Charset", "utf-8"); conn.setRequestProperty("contentType", "utf-8"); conn.setDoOutput(true); conn.setDoInput(true); out = new PrintWriter(conn.getOutputStream()); out.print(param); out.flush(); in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); String line; while ((line = in.readLine()) != null) { result.append(line); } log.info("recv - {}", result); } catch (ConnectException e) { log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); } catch (SocketTimeoutException e) { log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); } catch (IOException e) { log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); } catch (Exception e) { log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); } finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); } } return result.toString(); } public static String sendSSLPost(String url, String param) { StringBuilder result = new StringBuilder(); String urlNameString = url + "?" + param; try { log.info("sendSSLPost - {}", urlNameString); SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); URL console = new URL(urlNameString); HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); conn.setRequestProperty("Accept-Charset", "utf-8"); conn.setRequestProperty("contentType", "utf-8"); conn.setDoOutput(true); conn.setDoInput(true); conn.setSSLSocketFactory(sc.getSocketFactory()); conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); conn.connect(); InputStream is = conn.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String ret = ""; while ((ret = br.readLine()) != null) { if (ret != null && !"".equals(ret.trim())) { result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); } } log.info("recv - {}", result); conn.disconnect(); br.close(); } catch (ConnectException e) { log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); } catch (SocketTimeoutException e) { log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); } catch (IOException e) { log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); } catch (Exception e) { log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); } return result.toString(); } private static class TrustAnyTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } private static class TrustAnyHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { return true; } } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/ip/AddressUtils.java ================================================ package ginyi.common.utils.ip; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import ginyi.common.utils.Constants; import ginyi.common.utils.StringUtils; import ginyi.common.utils.http.HttpUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 获取地址类 * * @author ruoyi */ public class AddressUtils { private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); // IP地址查询 public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; // 未知地址 public static final String UNKNOWN = "XX XX"; public static String getRealAddressByIP(String ip) { // 内网不查询 if (IpUtils.internalIp(ip)) { return "内网IP"; } try { String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); if (StringUtils.isEmpty(rspStr)) { log.error("获取地理位置异常 {}", ip); return UNKNOWN; } JSONObject obj = JSON.parseObject(rspStr); String region = obj.getString("pro"); String city = obj.getString("city"); return String.format("%s %s", region, city); } catch (Exception e) { log.error("获取地理位置异常 {}", ip); } return UNKNOWN; } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/ip/IpUtils.java ================================================ package ginyi.common.utils.ip; import ginyi.common.utils.StringUtils; import javax.servlet.http.HttpServletRequest; import java.net.InetAddress; import java.net.UnknownHostException; /** * 获取IP方法 * * @author ruoyi */ public class IpUtils { /** * 获取客户端IP * * @param request 请求对象 * @return IP地址 */ public static String getIpAddr(HttpServletRequest request) { if (request == null) { return "unknown"; } String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Forwarded-For"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); } /** * 检查是否为内部IP地址 * * @param ip IP地址 * @return 结果 */ public static boolean internalIp(String ip) { byte[] addr = textToNumericFormatV4(ip); return internalIp(addr) || "127.0.0.1".equals(ip); } /** * 检查是否为内部IP地址 * * @param addr byte地址 * @return 结果 */ private static boolean internalIp(byte[] addr) { if (StringUtils.isNull(addr) || addr.length < 2) { return true; } final byte b0 = addr[0]; final byte b1 = addr[1]; // 10.x.x.x/8 final byte SECTION_1 = 0x0A; // 172.16.x.x/12 final byte SECTION_2 = (byte) 0xAC; final byte SECTION_3 = (byte) 0x10; final byte SECTION_4 = (byte) 0x1F; // 192.168.x.x/16 final byte SECTION_5 = (byte) 0xC0; final byte SECTION_6 = (byte) 0xA8; switch (b0) { case SECTION_1: return true; case SECTION_2: if (b1 >= SECTION_3 && b1 <= SECTION_4) { return true; } case SECTION_5: switch (b1) { case SECTION_6: return true; } default: return false; } } /** * 将IPv4地址转换成字节 * * @param text IPv4地址 * @return byte 字节 */ public static byte[] textToNumericFormatV4(String text) { if (text.length() == 0) { return null; } byte[] bytes = new byte[4]; String[] elements = text.split("\\.", -1); try { long l; int i; switch (elements.length) { case 1: l = Long.parseLong(elements[0]); if ((l < 0L) || (l > 4294967295L)) { return null; } bytes[0] = (byte) (int) (l >> 24 & 0xFF); bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); bytes[3] = (byte) (int) (l & 0xFF); break; case 2: l = Integer.parseInt(elements[0]); if ((l < 0L) || (l > 255L)) { return null; } bytes[0] = (byte) (int) (l & 0xFF); l = Integer.parseInt(elements[1]); if ((l < 0L) || (l > 16777215L)) { return null; } bytes[1] = (byte) (int) (l >> 16 & 0xFF); bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); bytes[3] = (byte) (int) (l & 0xFF); break; case 3: for (i = 0; i < 2; ++i) { l = Integer.parseInt(elements[i]); if ((l < 0L) || (l > 255L)) { return null; } bytes[i] = (byte) (int) (l & 0xFF); } l = Integer.parseInt(elements[2]); if ((l < 0L) || (l > 65535L)) { return null; } bytes[2] = (byte) (int) (l >> 8 & 0xFF); bytes[3] = (byte) (int) (l & 0xFF); break; case 4: for (i = 0; i < 4; ++i) { l = Integer.parseInt(elements[i]); if ((l < 0L) || (l > 255L)) { return null; } bytes[i] = (byte) (int) (l & 0xFF); } break; default: return null; } } catch (NumberFormatException e) { return null; } return bytes; } /** * 获取IP地址 * * @return 本地IP地址 */ public static String getHostIp() { try { return InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { } return "127.0.0.1"; } /** * 获取主机名 * * @return 本地主机名 */ public static String getHostName() { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { } return "未知"; } /** * 从多级反向代理中获得第一个非unknown IP地址 * * @param ip 获得的IP地址 * @return 第一个非unknown IP地址 */ public static String getMultistageReverseProxyIp(String ip) { // 多级反向代理检测 if (ip != null && ip.indexOf(",") > 0) { final String[] ips = ip.trim().split(","); for (String subIp : ips) { if (false == isUnknown(subIp)) { ip = subIp; break; } } } return ip; } /** * 检测给定字符串是否为未知,多用于检测HTTP请求相关 * * @param checkString 被检测的字符串 * @return 是否未知 */ public static boolean isUnknown(String checkString) { return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/spring/SpringUtils.java ================================================ package ginyi.common.utils.spring; import ginyi.common.utils.StringUtils; import org.springframework.aop.framework.AopContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * spring工具类 方便在非spring管理环境中获取bean * * @author ruoyi */ @Component public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware { /** * Spring应用上下文环境 */ private static ConfigurableListableBeanFactory beanFactory; private static ApplicationContext applicationContext; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { SpringUtils.beanFactory = beanFactory; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtils.applicationContext = applicationContext; } /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws org.springframework.beans.BeansException */ @SuppressWarnings("unchecked") public static T getBean(String name) throws BeansException { return (T) beanFactory.getBean(name); } /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws org.springframework.beans.BeansException */ public static T getBean(Class clz) throws BeansException { T result = (T) beanFactory.getBean(clz); return result; } /** * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name) { return beanFactory.containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return boolean * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return beanFactory.isSingleton(name); } /** * @param name * @return Class 注册对象的类型 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException */ public static Class getType(String name) throws NoSuchBeanDefinitionException { return beanFactory.getType(name); } /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name * @return * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return beanFactory.getAliases(name); } /** * 获取aop代理对象 * * @param invoker * @return */ @SuppressWarnings("unchecked") public static T getAopProxy(T invoker) { return (T) AopContext.currentProxy(); } /** * 获取当前的环境配置,无配置返回null * * @return 当前的环境配置 */ public static String[] getActiveProfiles() { return applicationContext.getEnvironment().getActiveProfiles(); } /** * 获取当前的环境配置,当有多个环境配置时,只获取第一个 * * @return 当前的环境配置 */ public static String getActiveProfile() { final String[] activeProfiles = getActiveProfiles(); return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; } /** * 获取配置文件中的值 * * @param key 配置文件的key * @return 当前的配置文件的值 */ public static String getRequiredProperty(String key) { return applicationContext.getEnvironment().getRequiredProperty(key); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/text/CharsetKit.java ================================================ package ginyi.common.utils.text; import ginyi.common.utils.StringUtils; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** * 字符集工具类 * * @author ruoyi */ public class CharsetKit { /** * ISO-8859-1 */ public static final String ISO_8859_1 = "ISO-8859-1"; /** * UTF-8 */ public static final String UTF_8 = "UTF-8"; /** * GBK */ public static final String GBK = "GBK"; /** * ISO-8859-1 */ public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); /** * UTF-8 */ public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); /** * GBK */ public static final Charset CHARSET_GBK = Charset.forName(GBK); /** * 转换为Charset对象 * * @param charset 字符集,为空则返回默认字符集 * @return Charset */ public static Charset charset(String charset) { return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); } /** * 转换字符串的字符集编码 * * @param source 字符串 * @param srcCharset 源字符集,默认ISO-8859-1 * @param destCharset 目标字符集,默认UTF-8 * @return 转换后的字符集 */ public static String convert(String source, String srcCharset, String destCharset) { return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); } /** * 转换字符串的字符集编码 * * @param source 字符串 * @param srcCharset 源字符集,默认ISO-8859-1 * @param destCharset 目标字符集,默认UTF-8 * @return 转换后的字符集 */ public static String convert(String source, Charset srcCharset, Charset destCharset) { if (null == srcCharset) { srcCharset = StandardCharsets.ISO_8859_1; } if (null == destCharset) { destCharset = StandardCharsets.UTF_8; } if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) { return source; } return new String(source.getBytes(srcCharset), destCharset); } /** * @return 系统字符集编码 */ public static String systemCharset() { return Charset.defaultCharset().name(); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/text/Convert.java ================================================ package ginyi.common.utils.text; import ginyi.common.utils.StringUtils; import org.apache.commons.lang3.ArrayUtils; import java.nio.ByteBuffer; import java.nio.charset.Charset; public class Convert { /** * 转换为字符串
* 如果给定的值为null,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static String toStr(Object value, String defaultValue) { if (null == value) { return defaultValue; } if (value instanceof String) { return (String) value; } return value.toString(); } /** * 转换为字符串
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static String toStr(Object value) { return toStr(value, null); } /** * 转换为boolean
* 如果给定的值为空,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Boolean toBool(Object value) { return toBool(value, null); } /** * 转换为boolean
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Boolean toBool(Object value, Boolean defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Boolean) { return (Boolean) value; } String valueStr = toStr(value, null); if (StringUtils.isEmpty(valueStr)) { return defaultValue; } valueStr = valueStr.trim().toLowerCase(); switch (valueStr) { case "true": case "yes": case "ok": case "1": return true; case "false": case "no": case "0": return false; default: return defaultValue; } } /** * 转换为int
* 如果给定的值为null,或者转换失败,返回默认值null
* 转换失败不会报错 * * @param value 被转换的值 * @return 结果 */ public static Integer toInt(Object value) { return toInt(value, null); } /** * 转换为int
* 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错 * * @param value 被转换的值 * @param defaultValue 转换错误时的默认值 * @return 结果 */ public static Integer toInt(Object value, Integer defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Integer) { return (Integer) value; } if (value instanceof Number) { return ((Number) value).intValue(); } final String valueStr = toStr(value, null); if (StringUtils.isEmpty(valueStr)) { return defaultValue; } try { return Integer.parseInt(valueStr.trim()); } catch (Exception e) { return defaultValue; } } /** * 将对象转为字符串
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 * * @param obj 对象 * @return 字符串 */ public static String utf8Str(Object obj) { return str(obj, CharsetKit.CHARSET_UTF_8); } /** * 将对象转为字符串
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 * * @param obj 对象 * @param charset 字符集 * @return 字符串 */ public static String str(Object obj, Charset charset) { if (null == obj) { return null; } if (obj instanceof String) { return (String) obj; } else if (obj instanceof byte[]) { return str((byte[]) obj, charset); } else if (obj instanceof Byte[]) { byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); return str(bytes, charset); } else if (obj instanceof ByteBuffer) { return str((ByteBuffer) obj, charset); } return obj.toString(); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/text/StrFormatter.java ================================================ package ginyi.common.utils.text; import ginyi.common.utils.StringUtils; /** * 字符串格式化 * * @author ruoyi */ public class StrFormatter { public static final String EMPTY_JSON = "{}"; public static final char C_BACKSLASH = '\\'; public static final char C_DELIM_START = '{'; public static final char C_DELIM_END = '}'; /** * 格式化字符串
* 此方法只是简单将占位符 {} 按照顺序替换为参数
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
* 例:
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
* * @param strPattern 字符串模板 * @param argArray 参数列表 * @return 结果 */ public static String format(final String strPattern, final Object... argArray) { if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) { return strPattern; } final int strPatternLength = strPattern.length(); // 初始化定义好的长度以获得更好的性能 StringBuilder sbuf = new StringBuilder(strPatternLength + 50); int handledPosition = 0; int delimIndex;// 占位符所在位置 for (int argIndex = 0; argIndex < argArray.length; argIndex++) { delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); if (delimIndex == -1) { if (handledPosition == 0) { return strPattern; } else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 sbuf.append(strPattern, handledPosition, strPatternLength); return sbuf.toString(); } } else { if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) { if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) { // 转义符之前还有一个转义符,占位符依旧有效 sbuf.append(strPattern, handledPosition, delimIndex - 1); sbuf.append(Convert.utf8Str(argArray[argIndex])); handledPosition = delimIndex + 2; } else { // 占位符被转义 argIndex--; sbuf.append(strPattern, handledPosition, delimIndex - 1); sbuf.append(C_DELIM_START); handledPosition = delimIndex + 1; } } else { // 正常占位符 sbuf.append(strPattern, handledPosition, delimIndex); sbuf.append(Convert.utf8Str(argArray[argIndex])); handledPosition = delimIndex + 2; } } } // 加入最后一个占位符后所有的字符 sbuf.append(strPattern, handledPosition, strPattern.length()); return sbuf.toString(); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/uuid/IdUtils.java ================================================ package ginyi.common.utils.uuid; /** * ID生成器工具类 * * @author ruoyi */ public class IdUtils { /** * 获取随机UUID * * @return 随机UUID */ public static String randomUUID() { return UUID.randomUUID().toString(); } /** * 简化的UUID,去掉了横线 * * @return 简化的UUID,去掉了横线 */ public static String simpleUUID() { return UUID.randomUUID().toString(true); } /** * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID * * @return 随机UUID */ public static String fastUUID() { return UUID.fastUUID().toString(); } /** * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID * * @return 简化的UUID,去掉了横线 */ public static String fastSimpleUUID() { return UUID.fastUUID().toString(true); } } ================================================ FILE: ginyi-springboot/ginyi-common/ginyi-common-utils/src/main/java/ginyi/common/utils/uuid/UUID.java ================================================ package ginyi.common.utils.uuid; import cn.hutool.core.exceptions.UtilException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; /** * 提供通用唯一识别码(universally unique identifier)(UUID)实现 * * @author ruoyi */ public final class UUID implements java.io.Serializable, Comparable { private static final long serialVersionUID = -1185015143654744140L; /** * SecureRandom 的单例 */ private static class Holder { static final SecureRandom numberGenerator = getSecureRandom(); } /** * 此UUID的最高64有效位 */ private final long mostSigBits; /** * 此UUID的最低64有效位 */ private final long leastSigBits; /** * 私有构造 * * @param data 数据 */ private UUID(byte[] data) { long msb = 0; long lsb = 0; assert data.length == 16 : "data must be 16 bytes in length"; for (int i = 0; i < 8; i++) { msb = (msb << 8) | (data[i] & 0xff); } for (int i = 8; i < 16; i++) { lsb = (lsb << 8) | (data[i] & 0xff); } this.mostSigBits = msb; this.leastSigBits = lsb; } /** * 使用指定的数据构造新的 UUID。 * * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 */ public UUID(long mostSigBits, long leastSigBits) { this.mostSigBits = mostSigBits; this.leastSigBits = leastSigBits; } /** * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。 * * @return 随机生成的 {@code UUID} */ public static UUID fastUUID() { return randomUUID(false); } /** * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 * * @return 随机生成的 {@code UUID} */ public static UUID randomUUID() { return randomUUID(true); } /** * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 * * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 * @return 随机生成的 {@code UUID} */ public static UUID randomUUID(boolean isSecure) { final Random ng = isSecure ? Holder.numberGenerator : getRandom(); byte[] randomBytes = new byte[16]; ng.nextBytes(randomBytes); randomBytes[6] &= 0x0f; /* clear version */ randomBytes[6] |= 0x40; /* set to version 4 */ randomBytes[8] &= 0x3f; /* clear variant */ randomBytes[8] |= 0x80; /* set to IETF variant */ return new UUID(randomBytes); } /** * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 * * @param name 用于构造 UUID 的字节数组。 * @return 根据指定数组生成的 {@code UUID} */ public static UUID nameUUIDFromBytes(byte[] name) { MessageDigest md; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException nsae) { throw new InternalError("MD5 not supported"); } byte[] md5Bytes = md.digest(name); md5Bytes[6] &= 0x0f; /* clear version */ md5Bytes[6] |= 0x30; /* set to version 3 */ md5Bytes[8] &= 0x3f; /* clear variant */ md5Bytes[8] |= 0x80; /* set to IETF variant */ return new UUID(md5Bytes); } /** * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 * * @param name 指定 {@code UUID} 字符串 * @return 具有指定值的 {@code UUID} * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 */ public static UUID fromString(String name) { String[] components = name.split("-"); if (components.length != 5) { throw new IllegalArgumentException("Invalid UUID string: " + name); } for (int i = 0; i < 5; i++) { components[i] = "0x" + components[i]; } long mostSigBits = Long.decode(components[0]).longValue(); mostSigBits <<= 16; mostSigBits |= Long.decode(components[1]).longValue(); mostSigBits <<= 16; mostSigBits |= Long.decode(components[2]).longValue(); long leastSigBits = Long.decode(components[3]).longValue(); leastSigBits <<= 48; leastSigBits |= Long.decode(components[4]).longValue(); return new UUID(mostSigBits, leastSigBits); } /** * 返回此 UUID 的 128 位值中的最低有效 64 位。 * * @return 此 UUID 的 128 位值中的最低有效 64 位。 */ public long getLeastSignificantBits() { return leastSigBits; } /** * 返回此 UUID 的 128 位值中的最高有效 64 位。 * * @return 此 UUID 的 128 位值中最高有效 64 位。 */ public long getMostSignificantBits() { return mostSigBits; } /** * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 *

* 版本号具有以下含意: *

    *
  • 1 基于时间的 UUID *
  • 2 DCE 安全 UUID *
  • 3 基于名称的 UUID *
  • 4 随机生成的 UUID *
* * @return 此 {@code UUID} 的版本号 */ public int version() { // Version is bits masked by 0x000000000000F000 in MS long return (int) ((mostSigBits >> 12) & 0x0f); } /** * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 *

* 变体号具有以下含意: *

    *
  • 0 为 NCS 向后兼容保留 *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 *
  • 6 保留,微软向后兼容 *
  • 7 保留供以后定义使用 *
* * @return 此 {@code UUID} 相关联的变体号 */ public int variant() { // This field is composed of a varying number of bits. // 0 - - Reserved for NCS backward compatibility // 1 0 - The IETF aka Leach-Salz variant (used by this class) // 1 1 0 Reserved, Microsoft backward compatibility // 1 1 1 Reserved for future definition. return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); } /** * 与此 UUID 相关联的时间戳值。 * *

* 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
* 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 * *

* 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
* 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 * * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 */ public long timestamp() throws UnsupportedOperationException { checkTimeBase(); return (mostSigBits & 0x0FFFL) << 48// | ((mostSigBits >> 16) & 0x0FFFFL) << 32// | mostSigBits >>> 32; } /** * 与此 UUID 相关联的时钟序列值。 * *

* 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 *

* {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 * UnsupportedOperationException。 * * @return 此 {@code UUID} 的时钟序列 * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 */ public int clockSequence() throws UnsupportedOperationException { checkTimeBase(); return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); } /** * 与此 UUID 相关的节点值。 * *

* 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 *

* 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
* 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 * * @return 此 {@code UUID} 的节点值 * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 */ public long node() throws UnsupportedOperationException { checkTimeBase(); return leastSigBits & 0x0000FFFFFFFFFFFFL; } /** * 返回此{@code UUID} 的字符串表现形式。 * *

* UUID 的字符串表示形式由此 BNF 描述: * *

     * {@code
     * UUID                   = ----
     * time_low               = 4*
     * time_mid               = 2*
     * time_high_and_version  = 2*
     * variant_and_sequence   = 2*
     * node                   = 6*
     * hexOctet               = 
     * hexDigit               = [0-9a-fA-F]
     * }
     * 
* * * * @return 此{@code UUID} 的字符串表现形式 * @see #toString(boolean) */ @Override public String toString() { return toString(false); } /** * 返回此{@code UUID} 的字符串表现形式。 * *

* UUID 的字符串表示形式由此 BNF 描述: * *

     * {@code
     * UUID                   = ----
     * time_low               = 4*
     * time_mid               = 2*
     * time_high_and_version  = 2*
     * variant_and_sequence   = 2*
     * node                   = 6*
     * hexOctet               = 
     * hexDigit               = [0-9a-fA-F]
     * }
     * 
* * * * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 * @return 此{@code UUID} 的字符串表现形式 */ public String toString(boolean isSimple) { final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); // time_low builder.append(digits(mostSigBits >> 32, 8)); if (!isSimple) { builder.append('-'); } // time_mid builder.append(digits(mostSigBits >> 16, 4)); if (!isSimple) { builder.append('-'); } // time_high_and_version builder.append(digits(mostSigBits, 4)); if (!isSimple) { builder.append('-'); } // variant_and_sequence builder.append(digits(leastSigBits >> 48, 4)); if (!isSimple) { builder.append('-'); } // node builder.append(digits(leastSigBits, 12)); return builder.toString(); } /** * 返回此 UUID 的哈希码。 * * @return UUID 的哈希码值。 */ @Override public int hashCode() { long hilo = mostSigBits ^ leastSigBits; return ((int) (hilo >> 32)) ^ (int) hilo; } /** * 将此对象与指定对象比较。 *

* 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 * * @param obj 要与之比较的对象 * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} */ @Override public boolean equals(Object obj) { if ((null == obj) || (obj.getClass() != UUID.class)) { return false; } UUID id = (UUID) obj; return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); } // Comparison Operations /** * 将此 UUID 与指定的 UUID 比较。 * *

* 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 * * @param val 与此 UUID 比较的 UUID * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 */ @Override public int compareTo(UUID val) { // The ordering is intentionally set up so that the UUIDs // can simply be numerically compared as two numbers return (this.mostSigBits < val.mostSigBits ? -1 : // (this.mostSigBits > val.mostSigBits ? 1 : // (this.leastSigBits < val.leastSigBits ? -1 : // (this.leastSigBits > val.leastSigBits ? 1 : // 0)))); } // ------------------------------------------------------------------------------------------------------------------- // Private method start /** * 返回指定数字对应的hex值 * * @param val 值 * @param digits 位 * @return 值 */ private static String digits(long val, int digits) { long hi = 1L << (digits * 4); return Long.toHexString(hi | (val & (hi - 1))).substring(1); } /** * 检查是否为time-based版本UUID */ private void checkTimeBase() { if (version() != 1) { throw new UnsupportedOperationException("Not a time-based UUID"); } } /** * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) * * @return {@link SecureRandom} */ public static SecureRandom getSecureRandom() { try { return SecureRandom.getInstance("SHA1PRNG"); } catch (NoSuchAlgorithmException e) { throw new UtilException(e); } } /** * 获取随机数生成器对象
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 * * @return {@link ThreadLocalRandom} */ public static ThreadLocalRandom getRandom() { return ThreadLocalRandom.current(); } } ================================================ FILE: ginyi-springboot/ginyi-common/pom.xml ================================================ ginyi-springboot com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-common pom ginyi-common-mysql ginyi-common-redis ginyi-common-exception ginyi-common-result ginyi-common-utils ginyi-common-swagger ginyi-common-constants ginyi-common-annotation ginyi-common-enums ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-core/pom.xml ================================================ ginyi-framework com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-framework-core 8 8 org.springframework.boot spring-boot-starter-aop ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-core/src/main/java/ginyi/framework/core/config/AppConfig.java ================================================ package ginyi.framework.core.config; import lombok.extern.slf4j.Slf4j; import org.fusesource.jansi.Ansi; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import java.net.InetAddress; import java.net.UnknownHostException; @Slf4j public class AppConfig { /** * 彩色打印字体 */ public static String colorPrint(String s, Ansi.Color color) { return Ansi.ansi().eraseScreen().fg(color).a(s).reset().toString(); } public static void printAppInfo(ConfigurableApplicationContext context) throws UnknownHostException { // 获取环境配置信息 ConfigurableEnvironment env = context.getEnvironment(); String projectName = env.getProperty("ginyi.project-name"); String contextPath = env.getProperty("server.servlet.context-path"); String port = env.getProperty("server.port"); String baseUrl = "http://" + InetAddress.getLocalHost().getHostAddress() + ":" + port + contextPath; String swaggerDoc = baseUrl + "swagger-ui.html"; String knife4jDoc = baseUrl + "doc.html"; String startSuccess = "\n" + " _______ __ __ __ ____ ____ __ \n" + " / _____|| | | \\ | | \\ \\ / / | | 项目名称:" + projectName + "\n" + "| | __ | | | \\| | \\ \\/ / | | 服务地址:" + baseUrl + "\n" + "| | |_ | | | | | \\_ _/ | | 接口文档:" + knife4jDoc + " or " + swaggerDoc + "\n" + "| |__| | | | | |\\ | | | | | 如果你喜欢这个项目,欢迎Star!https://gitee.com/Ginyi/ginyi-spring-vue \n" + " \\______| |__| |__| \\__| |__| |__| 启动成功...\n"; log.info("\n{}", colorPrint(startSuccess, Ansi.Color.CYAN)); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-core/src/main/java/ginyi/framework/core/config/GinyiConfig.java ================================================ package ginyi.framework.core.config; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 读取项目相关配置 * * @author ruoyi */ @Component public class GinyiConfig { /** * 上传路径 */ @Value("${ginyi.profile}") public static String profile; /** * 获取导入上传路径 */ public static String getImportPath() { return profile + "/import"; } /** * 获取头像上传路径 */ public static String getAvatarPath() { return profile + "/avatar"; } /** * 获取下载路径 */ public static String getDownloadPath() { return profile + "/download/"; } /** * 获取上传路径 */ public static String getUploadPath() { return profile + "/upload"; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/pom.xml ================================================ ginyi-framework com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-framework-security 8 8 org.springframework.boot spring-boot-starter-security com.ginyi ginyi-framework-core ${ginyi.version} eu.bitwalker UserAgentUtils com.ginyi ginyi-common-enums ${ginyi.version} ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/aspectj/LogAspect.java ================================================ package ginyi.framework.security.aspectj; import cn.hutool.core.date.DateUtil; import com.alibaba.fastjson2.JSON; import ginyi.common.annotation.Log; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.enums.BusinessStatus; import ginyi.common.enums.HttpMethod; import ginyi.common.exception.CommonException; import ginyi.common.exception.UnPermissionException; import ginyi.common.exception.UserPasswordNotMatchException; import ginyi.common.exception.UserPasswordRetryLimitExceedException; import ginyi.common.result.StateCode; import ginyi.common.utils.ServletUtils; import ginyi.common.utils.StringUtils; import ginyi.common.utils.ip.IpUtils; import ginyi.framework.security.filter.PropertyPreExcludeFilter; import ginyi.framework.security.manager.AsyncManager; import ginyi.framework.security.manager.factory.AsyncFactory; import ginyi.framework.security.utils.SecurityUtils; import ginyi.system.domain.LoginUser; import ginyi.system.domain.SysLogOperation; 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.fusesource.jansi.Ansi; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.*; /** * 操作日志记录处理 * * @author ruoyi */ @Aspect @Component @Slf4j 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 { // 获取当前的用户 LoginUser loginUser = SecurityUtils.getLoginUser(); // 数据库日志 SysLogOperation operationLog = new SysLogOperation(); operationLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 请求的地址 String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); operationLog.setOperationIp(ip); operationLog.setOperationUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); operationLog.setCreateTime(new Date()); if (loginUser != null) { operationLog.setOperationName(loginUser.getUsername()); } // 设置方法名称 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); operationLog.setMethod(className + "." + methodName + "()"); // 设置请求方式 operationLog.setRequestMethod(ServletUtils.getRequest().getMethod()); // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, controllerLog, operationLog, jsonResult); log.info("[ Log日志记录 ] 操作人员: {}", operationLog.getOperationName()); log.info("[ Log日志记录 ] 操作主机: {}", operationLog.getOperationIp()); log.info("[ Log日志记录 ] 请求时间: {}", DateUtil.date(System.currentTimeMillis())); log.info("[ Log日志记录 ] 请求模块: {}", operationLog.getTitle()); log.info("[ Log日志记录 ] 请求地址: {}", operationLog.getOperationUrl()); log.info("[ Log日志记录 ] 方法类型: {}", operationLog.getRequestMethod()); log.info("[ Log日志记录 ] 操作类型: {}", operationLog.getBusinessType() == 0 ? "其他" : operationLog.getBusinessType() == 1 ? "新增" : operationLog.getBusinessType() == 2 ? "修改" : "删除"); log.info("[ Log日志记录 ] 方法名称: {}", operationLog.getMethod()); log.info("[ Log日志记录 ] 请求参数: {}", operationLog.getOperationParam()); if (e != null) { String msg = handleException(e); operationLog.setStatus(BusinessStatus.FAIL.ordinal()); operationLog.setErrorMsg(msg); log.info("[ Log日志记录 ] 请求结果: {}", operationLog.getStatus() == 0 ? colorPrint("成功", Ansi.Color.GREEN) : colorPrint("失败", Ansi.Color.RED)); log.info("[ Log日志记录 ] 响应结果: {}\n", msg); } else { log.info("[ Log日志记录 ] 请求结果: {}", operationLog.getStatus() == 0 ? colorPrint("成功", Ansi.Color.GREEN) : colorPrint("失败", Ansi.Color.RED)); log.info("[ Log日志记录 ] 响应结果: {}\n", operationLog.getJsonResult()); } // 保存数据库 AsyncManager.me().execute(AsyncFactory.recordOper(operationLog)); } catch (Exception exp) { // 记录本地异常日志 log.error("记录本地日志时发生异常,异常信息:{}", exp.getMessage()); exp.printStackTrace(); } } /** * 处理并异常信息 * * @param e * @return */ private String handleException(Exception e) { HashMap map = new HashMap<>(); // 公用异常 if (e instanceof CommonException) { CommonException ce = (CommonException) e; map.put("code", ce.getState().getCode()); map.put("msg", ce.getState().getMessage()); map.put("data", ce.getData()); return JSON.toJSONString(map); } // 请求参数不合法 if (e instanceof HttpMessageNotReadableException) { HttpMessageNotReadableException he = (HttpMessageNotReadableException) e; map.put("code", StateCode.ERROR_REQUEST_PARAMS.getCode()); map.put("msg", StateCode.ERROR_REQUEST_PARAMS.getMessage()); map.put("data", CommonMessageConstants.SYS_REQUEST_ILLEGAL); return JSON.toJSONString(map); } // 用户名密码不匹配 if (e instanceof UserPasswordNotMatchException) { UserPasswordNotMatchException ue = (UserPasswordNotMatchException) e; map.put("code", StateCode.ERROR_UNAUTHENTICATION.getCode()); map.put("msg", StateCode.ERROR_UNAUTHENTICATION.getMessage()); map.put("data", CommonMessageConstants.USER_PASSWORD_NOT_MATCH); return JSON.toJSONString(map); } // 用户登录失败次数超最大限制异常 if (e instanceof UserPasswordRetryLimitExceedException) { UserPasswordRetryLimitExceedException upe = (UserPasswordRetryLimitExceedException) e; map.put("code", upe.getState().getCode()); map.put("msg", upe.getState().getMessage()); map.put("data", upe.getData()); return JSON.toJSONString(map); } // 上传文件异常 if (e instanceof MultipartException) { MultipartException mte = (MultipartException) e; map.put("code", StateCode.ERROR_MULTIPART.getCode()); map.put("msg", StateCode.ERROR_MULTIPART.getMessage()); if (mte instanceof MaxUploadSizeExceededException) { map.put("data", CommonMessageConstants.UPLOAD_SIZE_EXCEED); return JSON.toJSONString(map); } map.put("data", CommonMessageConstants.UPLOAD_FILE_ERROR); return JSON.toJSONString(map); } // 访问接口无权限 if (e instanceof UnPermissionException) { UnPermissionException upe = (UnPermissionException) e; map.put("code", StateCode.ERROR_NOT_PERMISSION.getCode()); map.put("msg", StateCode.ERROR_NOT_PERMISSION.getMessage()); map.put("data", CommonMessageConstants.SYS_ERROR); return JSON.toJSONString(map); } // 处理其他所有未知异常 map.put("code", StateCode.ERROR_SYSTEM.getCode()); map.put("msg", StateCode.ERROR_SYSTEM.getMessage()); map.put("data", CommonMessageConstants.SYS_ERROR); return JSON.toJSONString(map); } /** * 获取注解中对方法的描述信息 用于Controller层注解 * * @param log 日志 * @param operationLog 操作日志 * @throws Exception */ public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysLogOperation operationLog, Object jsonResult) throws Exception { // 设置action动作 operationLog.setBusinessType(log.businessType().ordinal()); // 设置标题 operationLog.setTitle(log.title()); // 设置操作人类别 operationLog.setOperatorType(log.operatorType().ordinal()); // 是否需要保存request,参数和值 if (log.isSaveRequestData()) { // 获取参数的信息,传入到数据库中。 setRequestValue(joinPoint, operationLog); } // 是否需要保存response,参数和值 if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) { operationLog.setJsonResult(JSON.toJSONString(jsonResult)); } } /** * 获取请求的参数,放到log中 * * @param operationLog 操作日志 * @throws Exception 异常 */ private void setRequestValue(JoinPoint joinPoint, SysLogOperation operationLog) throws Exception { String requestMethod = operationLog.getRequestMethod(); if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { String params = argsArrayToString(joinPoint.getArgs()); operationLog.setOperationParam(params); } else { Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); operationLog.setOperationParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter()), 0, 2000)); } } /** * 参数拼装 */ private String argsArrayToString(Object[] paramsArray) { String params = ""; if (paramsArray != null && paramsArray.length > 0) { for (Object o : paramsArray) { if (StringUtils.isNotNull(o) && !isFilterObject(o)) { try { String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter()); params += jsonObj.toString() + " "; } catch (Exception e) { } } } } return params.trim(); } /** * 忽略敏感属性 */ public PropertyPreExcludeFilter excludePropertyPreFilter() { return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES); } /** * 判断是否需要过滤的对象。 * * @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; } /** * 彩色打印字体 */ public static String colorPrint(String s, Ansi.Color color) { return Ansi.ansi().eraseScreen().fg(color).a(s).reset().toString(); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/config/ResourcesConfig.java ================================================ package ginyi.framework.security.config; import ginyi.framework.core.config.GinyiConfig; import ginyi.common.utils.Constants; import ginyi.framework.security.interceptor.PreviewEnvInterceptor; import ginyi.framework.security.interceptor.RepeatSubmitInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.CacheControl; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; /** * 通用配置 * * @author ruoyi */ @Configuration public class ResourcesConfig implements WebMvcConfigurer { @Resource private RepeatSubmitInterceptor repeatSubmitInterceptor; @Resource private PreviewEnvInterceptor previewEnvInterceptor; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { /** 本地文件上传路径 */ registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") .addResourceLocations("file:" + GinyiConfig.profile + "/"); /** swagger配置 */ registry.addResourceHandler("/swagger-ui/**") .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic()); ; } /** * 自定义拦截规则 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); registry.addInterceptor(previewEnvInterceptor).addPathPatterns("/**"); } /** * 跨域配置 */ @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: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/config/SecurityConfig.java ================================================ package ginyi.framework.security.config; import ginyi.framework.security.filter.JwtAuthenticationTokenFilter; import ginyi.framework.security.handle.AuthenticationEntryPointImpl; import ginyi.framework.security.handle.LogoutSuccessHandlerImpl; import ginyi.framework.security.properties.PermitAllUrlProperties; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.web.filter.CorsFilter; import javax.annotation.Resource; /** * spring security配置 * * @author ruoyi */ @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 自定义用户认证逻辑 */ @Resource private UserDetailsService userDetailsService; /** * 认证失败处理类 */ @Resource private AuthenticationEntryPointImpl unauthorizedHandler; /** * 退出处理类 */ @Resource private LogoutSuccessHandlerImpl logoutSuccessHandler; /** * token认证过滤器 */ @Resource private JwtAuthenticationTokenFilter authenticationTokenFilter; /** * 跨域过滤器 */ @Resource private CorsFilter corsFilter; /** * 允许匿名访问的地址 */ @Resource private PermitAllUrlProperties permitAllUrl; /** * 解决 无法直接注入 AuthenticationManager * * @return * @throws Exception */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * anyRequest | 匹配所有请求路径 * access | SpringEl表达式结果为true时可以访问 * anonymous | 匿名可以访问 * denyAll | 用户不能访问 * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 * hasRole | 如果有参数,参数表示角色,则其角色可以访问 * permitAll | 用户可以任意访问 * rememberMe | 允许通过remember-me登录的用户访问 * authenticated | 用户登录后可访问 */ @Override protected void configure(HttpSecurity httpSecurity) throws Exception { // 注解标记允许匿名访问的url ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); httpSecurity // CSRF禁用,因为不使用session .csrf().disable() // 禁用HTTP响应标头 .headers().cacheControl().disable().and() // 认证失败处理类 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 过滤请求 .authorizeRequests() // 对于登录login 注册register 验证码captchaImage 允许匿名访问 .antMatchers("/api/user/login", "/api/user/register", "/api/verify/**").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() // 放行websocket .antMatchers("/websocket/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); // 添加Logout filter httpSecurity.logout().logoutUrl("/**/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); } /** * 强散列哈希加密实现 */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 身份认证接口 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/config/ThreadPoolConfig.java ================================================ package ginyi.framework.security.config; import ginyi.common.utils.Threads; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; /** * 线程池配置 * * @author ruoyi **/ @Configuration public class ThreadPoolConfig { // 核心线程池大小 private int corePoolSize = 50; // 最大可创建的线程数 private int maxPoolSize = 200; // 队列最大长度 private int queueCapacity = 1000; // 线程池维护线程所允许的空闲时间 private int keepAliveSeconds = 300; @Bean(name = "threadPoolTaskExecutor") public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setMaxPoolSize(maxPoolSize); executor.setCorePoolSize(corePoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(keepAliveSeconds); // 线程池对拒绝任务(无线程可用)的处理策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } /** * 执行周期性或定时任务 */ @Bean(name = "scheduledExecutorService") protected ScheduledExecutorService scheduledExecutorService() { return new ScheduledThreadPoolExecutor(corePoolSize, new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), new ThreadPoolExecutor.CallerRunsPolicy()) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Threads.printException(r, t); } }; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/context/AuthenticationContextHolder.java ================================================ package ginyi.framework.security.context; import org.springframework.security.core.Authentication; /** * 身份验证信息 * * @author ruoyi */ public class AuthenticationContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal<>(); public static Authentication getContext() { return contextHolder.get(); } public static void setContext(Authentication context) { contextHolder.set(context); } public static void clearContext() { contextHolder.remove(); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/context/PermissionContextHolder.java ================================================ package ginyi.framework.security.context; import ginyi.common.utils.text.Convert; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; /** * 权限信息 * * @author ruoyi */ public class PermissionContextHolder { private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; public static void setContext(String permission) { RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, RequestAttributes.SCOPE_REQUEST); } public static String getContext() { return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, RequestAttributes.SCOPE_REQUEST)); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/filter/JwtAuthenticationTokenFilter.java ================================================ package ginyi.framework.security.filter; import ginyi.common.utils.StringUtils; import ginyi.framework.security.utils.SecurityUtils; import ginyi.system.domain.LoginUser; import ginyi.system.service.ITokenService; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.annotation.Resource; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * token过滤器 验证token有效性 * * @author ruoyi */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Resource private ITokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { LoginUser loginUser = tokenService.getLoginUser(request); if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { tokenService.verifyToken(loginUser); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } chain.doFilter(request, response); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/filter/PropertyPreExcludeFilter.java ================================================ package ginyi.framework.security.filter; import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; /** * 排除JSON敏感属性 * * @author ruoyi */ public class PropertyPreExcludeFilter extends SimplePropertyPreFilter { public PropertyPreExcludeFilter() { } public PropertyPreExcludeFilter addExcludes(String... filters) { for (int i = 0; i < filters.length; i++) { this.getExcludes().add(filters[i]); } return this; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/filter/RepeatedlyRequestWrapper.java ================================================ package ginyi.framework.security.filter; import ginyi.common.utils.Constants; import ginyi.common.utils.http.HttpHelper; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; /** * 构建可重复读取inputStream的request * * @author ruoyi */ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException { super(request); request.setCharacterEncoding(Constants.UTF8); response.setCharacterEncoding(Constants.UTF8); body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public int available() throws IOException { return body.length; } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/handle/AuthenticationEntryPointImpl.java ================================================ package ginyi.framework.security.handle; import com.alibaba.fastjson2.JSON; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.result.CommonResult; import ginyi.common.result.StateCode; import ginyi.common.utils.ServletUtils; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.Serializable; /** * 认证失败处理类 返回未授权 * * @author ruoyi */ @Component public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { ServletUtils.renderString(response, JSON.toJSONString(CommonResult.error(StateCode.ERROR_AUTHENTICATION_VALID, CommonMessageConstants.SYS_AUTHENTICATION_VALID))); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/handle/LogoutSuccessHandlerImpl.java ================================================ package ginyi.framework.security.handle; import com.alibaba.fastjson2.JSON; import ginyi.common.result.CommonResult; import ginyi.common.result.StateCode; import ginyi.common.utils.Constants; import ginyi.common.utils.ServletUtils; import ginyi.common.utils.StringUtils; import ginyi.framework.security.manager.AsyncManager; import ginyi.framework.security.manager.factory.AsyncFactory; import ginyi.system.domain.LoginUser; import ginyi.system.service.ITokenService; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 自定义退出处理类 返回成功 * * @author ruoyi */ @Configuration public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { @Resource private ITokenService tokenService; /** * 退出处理 * * @return */ @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { LoginUser loginUser = tokenService.getLoginUser(request); // 判断token是否存在 if (StringUtils.isNull(loginUser)) { ServletUtils.renderString(response, JSON.toJSONString(CommonResult.error(StateCode.ERROR_TIMEOUT_TOKEN))); } String userName = loginUser.getUsername(); // 删除用户缓存记录 tokenService.delLoginUser(loginUser.getToken()); // 记录用户退出日志 AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); ServletUtils.renderString(response, JSON.toJSONString(CommonResult.success())); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/interceptor/PreviewEnvInterceptor.java ================================================ package ginyi.framework.security.interceptor; import cn.hutool.crypto.SecureUtil; import cn.hutool.extra.servlet.ServletUtil; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.concurrent.TimeUnit; @Slf4j @Component public class PreviewEnvInterceptor implements HandlerInterceptor { @Resource private RedisCache redisCache; @Value("${ginyi.project-author}") private String author; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 不允许增删改数据 // boolean flag; // List list = Arrays.asList("add", "update", "edit", "delete", "remove", "register"); // for (String url : list) { // flag = request.getRequestURI().contains(url); // if (flag) { // throw new CommonException(StateCode.ERROR_LIMITED, CommonMessageConstants.SYS_PREVIEW_ENV); // } // } // 记录ip请求,防止被恶意请求接口 String clientIP = ServletUtil.getClientIP(request); String userAgent = ServletUtil.getHeader(request, "User-Agent", "utf-8"); String key = CacheConstants.API_REQUEST + SecureUtil.md5(clientIP + userAgent) + ":" + clientIP + ":" + request.getRequestURI(); Integer count = redisCache.getCacheObject(key, Integer.class); long expire = redisCache.getExpire(key); if (StringUtils.isNull(count)) { redisCache.setCacheObject(key, 1, 300, TimeUnit.SECONDS); } else { // 请求大于100次 if (count >= 100) { redisCache.setCacheObject(key, count + 1, 300, TimeUnit.SECONDS); throw new CommonException(StateCode.ERROR_LIMITED, CommonMessageConstants.SYS_BED_REQUEST); } else { redisCache.setCacheObject(key, count + 1, (int) expire, TimeUnit.SECONDS); } } return true; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/interceptor/RepeatSubmitInterceptor.java ================================================ package ginyi.framework.security.interceptor; import com.alibaba.fastjson2.JSON; import ginyi.common.annotation.RepeatSubmit; import ginyi.common.result.CommonResult; import ginyi.common.result.StateCode; import ginyi.common.utils.ServletUtils; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * 防止重复提交拦截器 * * @author ruoyi */ @Component public abstract class RepeatSubmitInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); if (annotation != null) { if (this.isRepeatSubmit(request, annotation)) { CommonResult commonResult = CommonResult.error(StateCode.ERROR_PARAMS_SERVICE, annotation.message()); ServletUtils.renderString(response, JSON.toJSONString(commonResult)); return false; } } return true; } else { return true; } } /** * 验证是否重复提交由子类实现具体的防重复提交的规则 * * @param request * @return * @throws Exception */ public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/interceptor/impl/SameUrlDataInterceptor.java ================================================ package ginyi.framework.security.interceptor.impl; import com.alibaba.fastjson2.JSON; import ginyi.common.annotation.RepeatSubmit; import ginyi.common.constant.CacheConstants; import ginyi.common.redis.cache.RedisCache; import ginyi.common.utils.StringUtils; import ginyi.common.utils.http.HttpHelper; import ginyi.framework.security.filter.RepeatedlyRequestWrapper; import ginyi.framework.security.interceptor.RepeatSubmitInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * 判断请求url和数据是否和上一次相同, * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 * * @author ruoyi */ @Component public class SameUrlDataInterceptor extends RepeatSubmitInterceptor { public final String REPEAT_PARAMS = "repeatParams"; public final String REPEAT_TIME = "repeatTime"; // 令牌自定义标识 @Value("${token.header}") private String header; @Autowired private RedisCache redisCache; @SuppressWarnings("unchecked") @Override public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) { String nowParams = ""; if (request instanceof RepeatedlyRequestWrapper) { RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; nowParams = HttpHelper.getBodyString(repeatedlyRequest); } // body参数为空,获取Parameter的数据 if (StringUtils.isEmpty(nowParams)) { nowParams = JSON.toJSONString(request.getParameterMap()); } Map nowDataMap = new HashMap(); nowDataMap.put(REPEAT_PARAMS, nowParams); nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); // 请求地址(作为存放cache的key值) String url = request.getRequestURI(); // 唯一值(没有消息头则使用请求地址) String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); // 唯一标识(指定key + url + 消息头) String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; Object sessionObj = redisCache.getCacheObject(cacheRepeatKey, Object.class); if (sessionObj != null) { Map sessionMap = (Map) sessionObj; if (sessionMap.containsKey(url)) { Map preDataMap = (Map) sessionMap.get(url); if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) { return true; } } } Map cacheMap = new HashMap(); cacheMap.put(url, nowDataMap); redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); return false; } /** * 判断参数是否相同 */ private boolean compareParams(Map nowMap, Map preMap) { String nowParams = (String) nowMap.get(REPEAT_PARAMS); String preParams = (String) preMap.get(REPEAT_PARAMS); return nowParams.equals(preParams); } /** * 判断两次间隔时间 */ private boolean compareTime(Map nowMap, Map preMap, int interval) { long time1 = (Long) nowMap.get(REPEAT_TIME); long time2 = (Long) preMap.get(REPEAT_TIME); if ((time1 - time2) < interval) { return true; } return false; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/manager/AsyncManager.java ================================================ package ginyi.framework.security.manager; import ginyi.common.utils.Threads; import ginyi.common.utils.spring.SpringUtils; import java.util.TimerTask; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * 异步任务管理器 * * @author ruoyi */ public class AsyncManager { /** * 操作延迟10毫秒 */ private final int OPERATE_DELAY_TIME = 10; /** * 异步操作任务调度线程池 */ private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); /** * 单例模式 */ private AsyncManager() { } private static AsyncManager me = new AsyncManager(); public static AsyncManager me() { return me; } /** * 执行任务 * * @param task 任务 */ public void execute(TimerTask task) { executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); } /** * 停止任务线程池 */ public void shutdown() { Threads.shutdownAndAwaitTermination(executor); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/manager/factory/AsyncFactory.java ================================================ package ginyi.framework.security.manager.factory; import eu.bitwalker.useragentutils.UserAgent; import ginyi.common.utils.LogUtils; import ginyi.common.utils.ServletUtils; import ginyi.common.utils.StringUtils; import ginyi.common.utils.Constants; import ginyi.common.utils.ip.AddressUtils; import ginyi.common.utils.ip.IpUtils; import ginyi.common.utils.spring.SpringUtils; import ginyi.system.domain.SysLogLogin; import ginyi.system.domain.SysLogOperation; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.mongodb.core.MongoTemplate; import java.util.Date; import java.util.TimerTask; /** * 异步工厂(产生任务用) * * @author ruoyi */ @Slf4j public class AsyncFactory { private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); /** * 记录登录信息 * * @param username 用户名 * @param status 状态 * @param message 消息 * @param args 列表 * @return 任务task */ public static TimerTask recordLogininfor(final String username, final String status, final String message, final Object... args) { final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); return new TimerTask() { @Override public void run() { String address = AddressUtils.getRealAddressByIP(ip); StringBuilder s = new StringBuilder(); s.append(LogUtils.getBlock(ip)); s.append(address); s.append(LogUtils.getBlock(username)); s.append(LogUtils.getBlock(status)); s.append(LogUtils.getBlock(message)); // 打印信息到日志 sys_user_logger.info(s.toString(), args); // 获取客户端操作系统 String os = userAgent.getOperatingSystem().getName(); // 获取客户端浏览器 String browser = userAgent.getBrowser().getName(); // 封装对象 SysLogLogin logininfor = new SysLogLogin(); logininfor.setUserName(username); logininfor.setIpaddr(ip); logininfor.setLoginLocation(address); logininfor.setBrowser(browser); logininfor.setOs(os); logininfor.setMsg(message); logininfor.setCreateTime(new Date()); // 日志状态 if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { logininfor.setStatus(Constants.SUCCESS); } else if (Constants.LOGIN_FAIL.equals(status)) { logininfor.setStatus(Constants.FAIL); } // 保存到mongo SpringUtils.getBean(MongoTemplate.class).save(logininfor); } }; } /** * 操作日志记录 * * @param operLog 操作日志信息 * @return 任务task */ public static TimerTask recordOper(final SysLogOperation operLog) { return new TimerTask() { @Override public void run() { // 远程查询操作地点 operLog.setOperationLocation(AddressUtils.getRealAddressByIP(operLog.getOperationIp())); SpringUtils.getBean(MongoTemplate.class).save(operLog); } }; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/properties/PermitAllUrlProperties.java ================================================ package ginyi.framework.security.properties; import ginyi.common.annotation.Anonymous; import org.apache.commons.lang3.RegExUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; /** * 设置Anonymous注解允许匿名访问的url * * @author ruoyi */ @Configuration public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware { private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); private ApplicationContext applicationContext; private List urls = new ArrayList<>(); public String ASTERISK = "*"; @Override public void afterPropertiesSet() { RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); Map map = mapping.getHandlerMethods(); map.keySet().forEach(info -> { HandlerMethod handlerMethod = map.get(info); // 获取方法上边的注解 替代path variable 为 * Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); // 获取类上边的注解, 替代path variable 为 * Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); }); } @Override public void setApplicationContext(ApplicationContext context) throws BeansException { this.applicationContext = context; } public List getUrls() { return urls; } public void setUrls(List urls) { this.urls = urls; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/ISysNoticeServiceImpl.java ================================================ package ginyi.framework.security.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.mysql.MyPage; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import ginyi.system.domain.LoginUser; import ginyi.system.domain.SysNotice; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.NoticeDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.NoticeVo; import ginyi.system.mapper.SysNoticeMapper; import ginyi.system.mapper.SysUserMapper; import ginyi.system.service.ISysNoticeService; import ginyi.system.service.ITokenService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; @Slf4j @Service public class ISysNoticeServiceImpl implements ISysNoticeService { @Resource private SysNoticeMapper noticeMapper; @Resource private SysUserMapper userMapper; @Resource private ITokenService tokenService; @Resource private HttpServletRequest request; @Resource private RedisCache redisCache; /** * 获取通知公告列表 */ @Override public BaseVo list(NoticeDto noticeDto, Long page, Long pageSize) { IPage list = noticeMapper.list(noticeDto, new MyPage(page, pageSize).getPage()); for (SysNotice notice : list.getRecords()) { // 如果 userReadIds 是 null,则把它设置为[] if (StringUtils.isNull(notice.getUserReadIds())) { notice.setUserReadIds(new ArrayList<>()); } } BaseVo baseVo = new BaseVo<>(); baseVo.setList(list.getRecords()); baseVo.setCount((int) list.getTotal()); return baseVo; } /** * 获取用户通知公告列表 */ @Override public BaseVo getUserNoticeList(Long page, Long pageSize) { LoginUser loginUser = tokenService.getLoginUser(request); Long userId = loginUser.getUserId(); IPage list = noticeMapper.getUserNoticeList(userId, new MyPage(page, pageSize).getPage()); ArrayList noticeList = new ArrayList<>(); for (SysNotice notice : list.getRecords()) { List userReadIds = notice.getUserReadIds(); NoticeVo vo = new NoticeVo(); BeanUtils.copyProperties(notice, vo); // 判断是否已读 if (StringUtils.isNotNull(userReadIds)) { vo.setHaveRead(userReadIds.contains(userId)); } noticeList.add(vo); } BaseVo baseVo = new BaseVo<>(); baseVo.setList(noticeList); baseVo.setCount((int) list.getTotal()); return baseVo; } /** * 发布通知公告 */ @Override public void add(NoticeDto noticeDto) { List userList = userMapper.selectList(null); if (noticeDto.getUserIds().size() > 0) { for (Long userId : noticeDto.getUserIds()) { boolean isExist = false; for (SysUser user : userList) { // 判断 userId 是否合法 if (userId.equals(user.getUserId())) { isExist = true; break; } } if (!isExist) { throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST); } } } else { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.USER_IDS_ILLEGAL); } noticeMapper.addNotice(noticeDto); } /** * 更新通知公告 * * @param noticeDto */ @Override public void updateNotice(NoticeDto noticeDto) { // 检查缓存中是否有标记空id if (redisCache.hasKey(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeDto.getNoticeId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeDto.getNoticeId() + CommonMessageConstants.NOTICE_NOT_EXIST); } SysNotice notice = noticeMapper.selectOne(noticeDto.getNoticeId()); if (StringUtils.isNull(notice)) { redisCache.setCacheObject(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeDto.getNoticeId(), noticeDto.getNoticeId()); throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeDto.getNoticeId() + CommonMessageConstants.NOTICE_NOT_EXIST); } // 如果有人确认收到该通知公告,则不允许修改 if (StringUtils.isNotNull(notice.getUserReadIds())) { throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.NOTICE_NOT_ALLOW); } noticeMapper.updateNotice(noticeDto); } /** * 确认收到通知公告 * * @param noticeId */ @Override public void haveRead(Long noticeId) { // 检查缓存中是否有标记空id if (redisCache.hasKey(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysNotice::getNoticeId, noticeId); SysNotice notice = noticeMapper.selectOne(noticeId); if (StringUtils.isNull(notice)) { redisCache.setCacheObject(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId, noticeId); throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST); } LoginUser loginUser = tokenService.getLoginUser(request); Long userId = loginUser.getUserId(); List userReadIds = notice.getUserReadIds(); // 如果为 null 则初始化为 [] if (StringUtils.isNull(userReadIds)) { userReadIds = new ArrayList<>(); } // 判断该通知公告的用户列表中是否存在该用户 if (!notice.getUserIds().contains(userId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.NOTICE_CURRENT_USER_NOT_EXIST); } // 判断是否已经确认收到该通知公告了 if (userReadIds.contains(userId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.NOTICE_HAVE_READ); } userReadIds.add(userId); notice.setUserReadIds(userReadIds); noticeMapper.haveRead(notice); } /** * 删除通知公告 * * @param noticeId */ @Override public void remove(Long noticeId) { // 检查缓存中是否有标记空id if (redisCache.hasKey(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST); } SysNotice notice = noticeMapper.selectOne(noticeId); if (StringUtils.isNull(notice)) { redisCache.setCacheObject(CacheConstants.NOTICE_NOT_EXIST_KEY + noticeId, noticeId); throw new CommonException(StateCode.ERROR_NOT_EXIST, noticeId + CommonMessageConstants.NOTICE_NOT_EXIST); } // 检查数据库中是否存在该通知公告 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysNotice::getNoticeId, noticeId); noticeMapper.delete(queryWrapper); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/MonitorServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.mysql.MyPage; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.system.domain.LoginUser; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.SessionUserVo; import ginyi.system.service.ISysOnlineService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.LinkedList; import java.util.List; import java.util.Set; @Slf4j @Service public class MonitorServiceImpl implements ISysOnlineService { @Resource private RedisCache redisCache; @Override public BaseVo getOnlineUserList(Long page, Long pageSize) { MyPage IPage = new MyPage(page, pageSize); long skipCount = IPage.getPageSize() * (IPage.getPageNum() - 1); BaseVo baseVo = new BaseVo<>(); List list = new LinkedList<>(); Set keys = redisCache.getKeys(CacheConstants.LOGIN_TOKEN_KEY + "*"); LoginUser loginUser; for (String key : keys) { SessionUserVo sessionUserVo = new SessionUserVo(); loginUser = redisCache.getCacheObject(key, LoginUser.class); BeanUtils.copyProperties(loginUser, sessionUserVo); list.add(sessionUserVo); } long toIndex = Math.min(skipCount + IPage.getPageSize(), list.size()); if(skipCount > list.size()){ list = new LinkedList<>(); }else { list = list.subList((int) skipCount, (int) toIndex); } baseVo.setList(list); baseVo.setCount(keys.size()); return baseVo; } @Override public void removeUser(String sessionId) { if(!redisCache.hasKey(CacheConstants.LOGIN_TOKEN_KEY + sessionId)){ throw new CommonException(StateCode.ERROR_NOT_EXIST, sessionId + CommonMessageConstants.USER_NOT_EXIST); } redisCache.removeCacheObject(CacheConstants.LOGIN_TOKEN_KEY + sessionId); } @Override public void removeUser(Set ids) { for (String sessionId : ids) { if(!redisCache.hasKey(CacheConstants.LOGIN_TOKEN_KEY + sessionId)){ throw new CommonException(StateCode.ERROR_NOT_EXIST, sessionId + CommonMessageConstants.USER_NOT_EXIST); } } // 全部没问题再执行 for (String sessionId : ids) { redisCache.removeCacheObject(CacheConstants.LOGIN_TOKEN_KEY + sessionId); } } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/PermissionService.java ================================================ package ginyi.framework.security.service; import ginyi.common.exception.UnPermissionException; import ginyi.common.utils.StringUtils; import ginyi.framework.security.context.PermissionContextHolder; import ginyi.framework.security.utils.SecurityUtils; import ginyi.system.domain.LoginUser; import ginyi.system.domain.SysRole; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.Set; /** * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 * * @author ruoyi */ @Service("ss") public class PermissionService { /** * 所有权限标识 */ private static final String ALL_PERMISSION = "*:*:*"; /** * 管理员角色权限标识 */ private static final String SUPER_ADMIN = "admin"; private static final String ROLE_DELIMETER = ","; private static final String PERMISSION_DELIMETER = ","; /** * 验证用户是否具备某权限 * * @param permission 权限字符串 * @return 用户是否具备某权限 */ public boolean hasPermission(String permission) { if (StringUtils.isEmpty(permission)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { return false; } PermissionContextHolder.setContext(permission); return hasPermissions(loginUser.getPermissions(), permission); } /** * 验证用户是否不具备某权限,与 hasPermi逻辑相反 * * @param permission 权限字符串 * @return 用户是否不具备某权限 */ public boolean lacksPermi(String permission) { return hasPermission(permission) != true; } /** * 验证用户是否具有以下任意一个权限 * * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 * @return 用户是否具有以下任意一个权限 */ public boolean hasAnyPermi(String permissions) { if (StringUtils.isEmpty(permissions)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { return false; } PermissionContextHolder.setContext(permissions); Set authorities = loginUser.getPermissions(); for (String permission : permissions.split(PERMISSION_DELIMETER)) { if (permission != null && hasPermissions(authorities, permission)) { return true; } } return false; } /** * 判断用户是否拥有某个角色 * * @param role 角色字符串 * @return 用户是否具备某角色 */ public boolean hasRole(String role) { if (StringUtils.isEmpty(role)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } for (SysRole sysRole : loginUser.getUser().getRoles()) { String roleKey = sysRole.getRoleKey(); if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) { return true; } } return false; } /** * 验证用户是否不具备某角色,与 isRole逻辑相反。 * * @param role 角色名称 * @return 用户是否不具备某角色 */ public boolean lacksRole(String role) { return hasRole(role) != true; } /** * 验证用户是否具有以下任意一个角色 * * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 * @return 用户是否具有以下任意一个角色 */ public boolean hasAnyRoles(String roles) { if (StringUtils.isEmpty(roles)) { return false; } LoginUser loginUser = SecurityUtils.getLoginUser(); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } for (String role : roles.split(ROLE_DELIMETER)) { if (hasRole(role)) { return true; } } return false; } /** * 判断是否包含权限 * * @param permissions 权限列表 * @param permission 权限字符串 * @return 用户是否具备某权限 */ private boolean hasPermissions(Set permissions, String permission) { boolean isHasPermission = permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); if (isHasPermission) { return true; } else { throw new UnPermissionException(); } } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysConfigServiceImpl.java ================================================ package ginyi.framework.security.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import ginyi.common.constant.CacheConstants; import ginyi.common.redis.cache.RedisCache; import ginyi.common.utils.StringUtils; import ginyi.common.utils.text.Convert; import ginyi.system.domain.SysConfig; import ginyi.system.mapper.SysConfigMapper; import ginyi.system.service.ISysConfigService; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class SysConfigServiceImpl implements ISysConfigService { @Resource private RedisCache redisCache; @Resource private SysConfigMapper configMapper; /** * 获取验证码开关 * * @return true开启,false关闭 */ @Override public boolean selectCaptchaEnabled() { String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); if (StringUtils.isEmpty(captchaEnabled)) { return true; } return Convert.toBool(captchaEnabled); } /** * 根据键名查询参数配置信息 * * @param configKey 参数key * @return 参数键值 */ @Override public String selectConfigByKey(String configKey) { String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey), String.class)); if (StringUtils.isNotEmpty(configValue)) { return configValue; } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysConfig::getConfigKey, configKey); SysConfig sysConfig = configMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(sysConfig)) { redisCache.setCacheObject(getCacheKey(configKey), sysConfig.getConfigValue()); return sysConfig.getConfigValue(); } return StringUtils.EMPTY; } /** * 设置cache key * * @param configKey 参数键 * @return 缓存键key */ private String getCacheKey(String configKey) { return CacheConstants.SYS_CONFIG_KEY + configKey; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysDeptServiceImpl.java ================================================ package ginyi.framework.security.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.mysql.MyPage; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import ginyi.system.domain.SysDept; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.DeptDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.DeptVo; import ginyi.system.mapper.SysDeptMapper; import ginyi.system.service.ISysDeptService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @Service @Slf4j public class SysDeptServiceImpl implements ISysDeptService { @Resource private SysDeptMapper deptMapper; @Resource private RedisCache redisCache; /** * 获取部门列表 * * @param deptDto * @param page * @param pageSize */ @Override public BaseVo list(DeptDto deptDto, Long page, Long pageSize) { IPage list = deptMapper.list(deptDto, new MyPage(page, pageSize).getPage()); // 转成树 List deptList = list.getRecords().stream() .filter(dept -> dept.getParentId().equals(0L)) .map(dept -> convertToDeptTree(dept, list.getRecords())).collect(Collectors.toList()); BaseVo baseVo = new BaseVo<>(); baseVo.setList(deptList); baseVo.setCount(deptList.size()); return baseVo; } /** * 根据部门id获取部门详情 * * @param deptId * @return */ @Override public DeptVo getDeptByDeptId(Long deptId) { // 检查缓存中是否存在空id if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST); } DeptVo deptVo = new DeptVo(); // 检查缓存中是否存在 SysDept dept = redisCache.getCacheObject(CacheConstants.DEPT_DETAILS_BY_DEPTID_KEY + deptId, SysDept.class); if (StringUtils.isNotNull(dept)) { BeanUtils.copyProperties(dept, deptVo); return deptVo; } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysDept::getDeptId, deptId); dept = deptMapper.selectOne(queryWrapper); if (StringUtils.isNull(dept)) { redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST); } redisCache.setCacheObject(CacheConstants.DEPT_DETAILS_BY_DEPTID_KEY + deptId, dept); BeanUtils.copyProperties(dept, deptVo); return deptVo; } /** * 新增部门 * * @param deptDto */ @Override public void addDept(DeptDto deptDto) { Long parentId = StringUtils.isNull(deptDto.getParentId()) ? 0L : deptDto.getParentId(); // 检查缓存中是否有标记同个分支下名称有被使用 if (redisCache.hasKey(CacheConstants.DEPT_NAME_USED_KEY + parentId + deptDto.getDeptName())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.DEPT_NAME_USED); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysDept::getParentId, parentId) .eq(SysDept::getDeptName, deptDto.getDeptName()); SysDept dept = deptMapper.selectOne(queryWrapper); // 检查同个分支下名称是否被使用 if (StringUtils.isNotNull(dept)) { redisCache.setCacheObject(CacheConstants.DEPT_NAME_USED_KEY + parentId + deptDto.getDeptName(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.DEPT_NAME_USED); } deptMapper.insertDept(deptDto); redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX); } /** * 更新部门 * * @param deptDto */ @Override public void updateDept(DeptDto deptDto) { // 检查缓存中是否有标记空id if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); Long parentId = StringUtils.isNull(deptDto.getParentId()) ? 0L : deptDto.getParentId(); queryWrapper.eq(parentId != 0L, SysDept::getDeptId, parentId); List deptList = deptMapper.selectList(queryWrapper); // 检查上级部门是否存在 if (deptList.size() == 0) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.DEPT_PARENT_NOT_EXIST); } queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysDept::getParentId, parentId) .eq(SysDept::getDeptName, deptDto.getDeptName()); SysDept dept = deptMapper.selectOne(queryWrapper); // 检查同个分支下名称是否被使用 if (StringUtils.isNotNull(dept)) { redisCache.setCacheObject(CacheConstants.DEPT_NAME_USED_KEY + parentId + deptDto.getDeptName(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.DEPT_NAME_USED); } queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysDept::getDeptId, deptDto.getDeptId()); dept = deptMapper.selectOne(queryWrapper); // 检查是否存在 if (StringUtils.isNull(dept)) { redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST); } deptMapper.updateDept(deptDto); redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX); } /** * 删除部门 * * @param deptId */ @Override public void removeDeptById(Long deptId) { // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysDept::getDeptId, deptId); SysDept dept = deptMapper.selectOne(queryWrapper); if (StringUtils.isNull(dept)) { redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST); } deptMapper.deleteById(deptId); redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX); } /** * 批量删除部门 * * @param ids */ @Override public void removeDeptByIds(Set ids) { if (ids.size() > 0) { List deptList; deptList = redisCache.getCacheList(CacheConstants.DEPT_LIST_KEY, SysDept.class); if (StringUtils.isNull(deptList) || deptList.size() == 0) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); deptList = deptMapper.selectList(queryWrapper); redisCache.setCacheList(CacheConstants.DEPT_LIST_KEY, deptList); } for (Long deptId : ids) { // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST); } boolean exist = deptList.stream().anyMatch(dept -> deptId.equals(dept.getDeptId())); if (!exist) { redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST); } } deptMapper.deleteBatchIds(ids); redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX); } else { throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } } /** * 更新状态 * @param deptDto */ @Override public void updateStatus(DeptDto deptDto) { if (StringUtils.isNull(deptDto.getDeptId())) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.DEPT_ID_NOT_FOUND); } // 状态参数是否合法 if (!("0".equals(deptDto.getStatus()) || "1".equals(deptDto.getStatus()))) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.DEPT_STATUS_ILLEGAL); } // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysDept::getDeptId, deptDto.getDeptId()); SysDept dept = deptMapper.selectOne(queryWrapper); if (StringUtils.isNull(dept)) { redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + deptDto.getDeptId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, deptDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST); } deptMapper.updateDeptStatus(deptDto); } /** * 部门列表转换成部门树的结构 * * @param dept * @param list * @return */ public SysDept convertToDeptTree(SysDept dept, List list) { SysDept sysDept = new SysDept(); BeanUtils.copyProperties(dept, sysDept); List children = list.stream() .filter(subDept -> dept.getDeptId().equals(subDept.getParentId())) .map(subDept -> convertToDeptTree(subDept, list)).collect(Collectors.toList()); sysDept.setChildren(children); return sysDept; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysLogServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.common.mysql.MyPage; import ginyi.system.domain.SysLogLogin; import ginyi.system.domain.SysLogOperation; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.service.ISysLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Slf4j @Service public class SysLogServiceImpl implements ISysLogService { @Resource private MongoTemplate mongoTemplate; @Override public BaseVo getLoginLogList(Long page, Long pageSize) { MyPage IPage = new MyPage(page, pageSize); BaseVo baseVo = new BaseVo<>(); Query query = new Query(); long count = mongoTemplate.count(query, SysLogLogin.class); query.skip((int) (IPage.getPageSize() * (IPage.getPageNum() - 1))); query.limit(IPage.getPageSize().intValue()); query.with(Sort.by(Sort.Order.desc("createTime"))); List list = mongoTemplate.find(query, SysLogLogin.class); baseVo.setList(list); baseVo.setCount((int) count); return baseVo; } @Override public BaseVo getOperationLogList(Long page, Long pageSize) { MyPage IPage = new MyPage(page, pageSize); BaseVo baseVo = new BaseVo<>(); Query query = new Query(); long count = mongoTemplate.count(query, SysLogOperation.class); int skip = (int) (IPage.getPageSize() * (IPage.getPageNum() - 1)); query.skip(skip).limit(IPage.getPageSize().intValue()); query.with(Sort.by(Sort.Order.desc("createTime"))); List list = mongoTemplate.find(query, SysLogOperation.class); baseVo.setList(list); baseVo.setCount((int) count); return baseVo; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysLoginLogServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.system.domain.SysLogLogin; import ginyi.system.service.ISysLogininforService; import org.springframework.stereotype.Service; import java.util.List; /** * 系统访问日志情况信息 服务层处理 * * @author ruoyi */ @Service public class SysLoginLogServiceImpl implements ISysLogininforService { /** * 新增系统登录日志 * * @param logininfor 访问日志对象 */ @Override public void insertLogininfor(SysLogLogin logininfor) { // todo 待写~ } /** * 查询系统登录日志集合 * * @param logininfor 访问日志对象 * @return 登录记录集合 */ @Override public List selectLogininforList(SysLogLogin logininfor) { // todo 待写~ return null; } /** * 批量删除系统登录日志 * * @param infoIds 需要删除的登录日志ID * @return 结果 */ @Override public int deleteLogininforByIds(Long[] infoIds) { // todo 待写~ return 0; } /** * 清空系统登录日志 */ @Override public void cleanLogininfor() { // todo 待写~ } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysLoginServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.constant.UserConstants; import ginyi.common.exception.CommonException; import ginyi.common.exception.UserPasswordNotMatchException; import ginyi.common.exception.UserPasswordRetryLimitExceedException; import ginyi.common.result.StateCode; import ginyi.common.utils.Constants; import ginyi.common.utils.DateUtils; import ginyi.common.utils.ServletUtils; import ginyi.common.utils.ip.IpUtils; import ginyi.framework.security.context.AuthenticationContextHolder; import ginyi.framework.security.manager.AsyncManager; import ginyi.framework.security.manager.factory.AsyncFactory; import ginyi.framework.security.utils.SecurityUtils; import ginyi.system.domain.LoginUser; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.LoginDto; import ginyi.system.domain.model.dto.RegisterDto; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.LoginVo; import ginyi.system.service.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class SysLoginServiceImpl implements ISysLoginService { @Value("${token.prefix}") private String tokenPrefix; @Value("${token.header}") private String tokenHeader; @Resource private ISysConfigService configService; @Resource private AuthenticationManager authenticationManager; @Resource private ISysUserService userService; @Resource private ITokenService tokenService; @Resource private IVerifyService verifyService; /** * 登录验证 * * @param loginDto * @return */ @Override public LoginVo login(LoginDto loginDto) { String username = loginDto.getUsername(); String password = loginDto.getPassword(); String code = loginDto.getCode(); boolean captchaEnabled = configService.selectCaptchaEnabled(); // 图片验证码开关 if (captchaEnabled) { verifyService.checkImgCode(code); } // 用户验证 Authentication authentication = null; try { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); AuthenticationContextHolder.setContext(authenticationToken); // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername authentication = authenticationManager.authenticate(authenticationToken); // 登录成功 LoginUser loginUser = (LoginUser) authentication.getPrincipal(); recordLoginInfo(loginUser.getUserId()); String token = tokenService.createToken(loginUser); // 生成token LoginVo loginVo = new LoginVo(); loginVo.setToken(tokenPrefix + " " +token); loginVo.setTokenHeader(tokenHeader); // 记录日志 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, CommonMessageConstants.LOGIN_SUCCESS)); return loginVo; } catch (Exception e) { if (e instanceof AuthenticationException) { // 账户被锁定 if (e.getCause() instanceof UserPasswordRetryLimitExceedException) { UserPasswordRetryLimitExceedException userPasswordRetryLimitExceedException = (UserPasswordRetryLimitExceedException) e.getCause(); AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, CommonMessageConstants.USER_IS_LOCKED)); throw new UserPasswordRetryLimitExceedException( userPasswordRetryLimitExceedException.getState(), userPasswordRetryLimitExceedException.getData() ); } // 密码不匹配 if (e.getCause() instanceof UserPasswordNotMatchException) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, CommonMessageConstants.USER_PASSWORD_NOT_MATCH)); throw new UserPasswordNotMatchException(); } // 其他异常(被禁用、被删除、被停用) if(e.getCause() instanceof CommonException){ CommonException commonException = (CommonException) e.getCause(); AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, String.valueOf(commonException.getData()))); throw new CommonException(commonException.getState(), commonException.getData()); } } else { // 其他的未知异常 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); throw new CommonException(StateCode.ERROR_SYSTEM); } } finally { AuthenticationContextHolder.clearContext(); } // 其他的未知异常 return null; } /** * 用户注册 * * @param registerDto */ @Override public void register(RegisterDto registerDto) { // 图片验证码开关 boolean captchaEnabled = configService.selectCaptchaEnabled(); if (captchaEnabled) { verifyService.checkImgCode(registerDto.getCode()); } SysUser sysUser = new SysUser(); sysUser.setUserName(registerDto.getUsername()); // 当前注册的用户是否存在 if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser))) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.USER_EXIST); } else { sysUser.setNickName(registerDto.getUsername()); sysUser.setPassword(SecurityUtils.encryptPassword(registerDto.getPassword())); boolean regFlag = userService.registerUser(sysUser); if (!regFlag) { throw new CommonException(StateCode.ERROR_SYSTEM); } else { AsyncManager.me().execute(AsyncFactory.recordLogininfor(registerDto.getUsername(), Constants.REGISTER, CommonMessageConstants.REGISTER_SUCCESS)); } } } /** * 记录登录信息 * * @param userId 用户ID */ public void recordLoginInfo(Long userId) { UserDto userDto = new UserDto(); userDto.setUserId(userId); userDto.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest())); userDto.setLoginDate(DateUtils.getNowDate()); userService.updateUser(userDto); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysMenuServiceImpl.java ================================================ package ginyi.framework.security.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import ginyi.framework.security.utils.SecurityUtils; import ginyi.system.domain.LoginUser; import ginyi.system.domain.SysMenu; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.MenuDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.mapper.SysMenuMapper; import ginyi.system.service.ISysMenuService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * 菜单 业务层处理 * * @author ruoyi */ @Slf4j @Service public class SysMenuServiceImpl implements ISysMenuService { @Resource private SysMenuMapper menuMapper; @Resource private RedisCache redisCache; /** * 根据角色ID查询权限 * * @param roleId 角色ID * @return 权限列表 */ @Override public Set selectMenuPermsByRoleId(Long roleId) { List perms = menuMapper.selectMenuPermsByRoleId(roleId); Set permsSet = new HashSet<>(); for (String perm : perms) { if (StringUtils.isNotEmpty(perm)) { permsSet.addAll(Arrays.asList(perm.trim().split(","))); } } return permsSet; } /** * 根据用户ID查询权限 * * @param userId 用户ID * @return 权限列表 */ @Override public Set selectMenuPermsByUserId(Long userId) { List perms = menuMapper.selectMenuPermsByUserId(userId); Set permsSet = new HashSet<>(); for (String perm : perms) { if (StringUtils.isNotEmpty(perm)) { permsSet.addAll(Arrays.asList(perm.trim().split(","))); } } return permsSet; } /** * 根据用户查询系统菜单列表 * * @return 菜单列表 */ @Override public BaseVo selectMenuList() { List menuList; BaseVo baseVo = new BaseVo<>(); LoginUser user = SecurityUtils.getLoginUser(); // 判断缓存是否有数据 menuList = redisCache.getCacheList(CacheConstants.MENU_USER_LIST_KEY + user.getUsername(), SysMenu.class); if (menuList.size() > 0) { baseVo.setList(menuList); baseVo.setCount(menuList.size()); return baseVo; } boolean isAdmin = SysUser.isAdmin(user.getUserId()); // 管理员返回全部,普通用户则对应的菜单 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysMenu::getMenuType, "M").or().eq(SysMenu::getMenuType, "C"); List list = isAdmin ? menuMapper.selectList(queryWrapper) : menuMapper.selectMenuListByUserId(user.getUserId()); // 转成树 menuList = list.stream() .filter(menu -> menu.getParentId().equals(0L)) .map(menu -> convertToMenuTree(menu, list)).collect(Collectors.toList()); redisCache.setCacheList(CacheConstants.MENU_USER_LIST_KEY + user.getUsername(), menuList); baseVo.setList(menuList); baseVo.setCount(menuList.size()); return baseVo; } /** * 管理员查询(管理)菜单列表 * * @param menuDto * @return */ @Override public BaseVo list(MenuDto menuDto) { List list = menuMapper.selectMenuListByAdmin(menuDto); List menuList = list.stream() .filter(menu -> menu.getParentId().equals(0L)) .map(menu -> convertToMenuTree(menu, list)).collect(Collectors.toList()); BaseVo baseVo = new BaseVo<>(); baseVo.setList(menuList); baseVo.setCount(menuList.size()); return baseVo; } /** * 菜单列表转成树状 * * @param menu * @param list * @return */ public SysMenu convertToMenuTree(SysMenu menu, List list) { SysMenu sysMenu = new SysMenu(); BeanUtils.copyProperties(menu, sysMenu); List children = list.stream() .filter(subMenu -> menu.getMenuId().equals(subMenu.getParentId())) .map(subMenu -> convertToMenuTree(subMenu, list)).collect(Collectors.toList()); sysMenu.setChildren(children); return sysMenu; } /** * 添加菜单 * * @param menuDto */ @Override public void addMenu(MenuDto menuDto) { // c 是菜单,其余的是目录或者按钮 if ("C".equalsIgnoreCase(menuDto.getMenuType())) { if (menuDto.getComponent().isEmpty()) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_COMPONENT_NOT_EXIST); } if (menuDto.getPath().isEmpty()) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_PATH_NOT_EXIST); } } // 判断是否已存在 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysMenu::getMenuName, menuDto.getMenuName()); SysMenu result = menuMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(result)) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.MENU_NAME_USED); } menuMapper.insertMenu(menuDto); // 清除menu的相关缓存 redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX); } /** * 根据id获取菜单详情 * * @param menuId * @return */ @Override public SysMenu getMenuById(Long menuId) { SysMenu menu; // 判断是否是无效id if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_NOT_EXIST); } // 查看缓存中是否有 menu = redisCache.getCacheObject(CacheConstants.MENU_DETAILS_BY_ID_KEY + menuId, SysMenu.class); if (StringUtils.isNotNull(menu)) { return menu; } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysMenu::getMenuId, menuId); menu = menuMapper.selectOne(queryWrapper); if (StringUtils.isNull(menu)) { // 存储不存在的key redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_NOT_EXIST); } // 存入缓存 redisCache.setCacheObject(CacheConstants.MENU_DETAILS_BY_ID_KEY + menuId, menu); return menu; } /** * 更新菜单 * * @param menuDto */ @Override public void updateMenu(MenuDto menuDto) { // c 是菜单,其余的是目录或者按钮 if ("C".equalsIgnoreCase(menuDto.getMenuType())) { if (StringUtils.isNull(menuDto.getComponent()) || menuDto.getComponent().isEmpty()) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_COMPONENT_NOT_EXIST); } if (StringUtils.isNull(menuDto.getPath()) || menuDto.getPath().isEmpty()) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_PATH_NOT_EXIST); } } // 判断menuId是否存在 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysMenu::getMenuId, menuDto.getMenuId()); SysMenu result = menuMapper.selectOne(queryWrapper); if (StringUtils.isNull(result)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_NOT_EXIST); } // 判断上级菜单是否存在 Long parentId = StringUtils.isNull(menuDto.getParentId()) ? 0L : menuDto.getParentId(); queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(parentId != 0L, SysMenu::getMenuId, parentId); List menuList = menuMapper.selectList(queryWrapper); if (menuList.size() == 0) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.MENU_PARENT_NOT_EXIST); } menuMapper.updateMenu(menuDto); // 清除menu的相关缓存 redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX); } /** * 根据菜单id删除 * * @param menuId */ @Override public void removeMenuById(Long menuId) { // 缓存中是否标记空id if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysMenu::getMenuId, menuId); SysMenu menu = menuMapper.selectOne(queryWrapper); // 数据不存在 if (StringUtils.isNull(menu)) { redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST); } menuMapper.deleteById(menuId); redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX); } /** * 根据ids批量删除菜单 * * @param ids */ @Override @Transactional public void removeMenuByIds(Set ids) { if (ids.size() > 0) { List menuList; menuList = redisCache.getCacheList(CacheConstants.MENU_LIST_KEY, SysMenu.class); if (StringUtils.isNull(menuList) || menuList.size() == 0) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); menuList = menuMapper.selectList(queryWrapper); } for (Long menuId : ids) { // 缓存中是否标记空id if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST); } boolean exist = menuList.stream().anyMatch(menu -> menuId.equals(menu.getMenuId())); if (!exist) { redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.MENU_NOT_EXIST); } } menuMapper.deleteBatchIds(ids); redisCache.removeCacheObject(CacheConstants.MENU_KEY_PREFIX); } else { throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } } /** * 更新状态 * @param menuDto */ @Override public void updateStatus(MenuDto menuDto) { if (StringUtils.isNull(menuDto.getMenuId())) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_ID_NOT_FOUND); } // 状态参数是否合法 if (!("0".equals(menuDto.getStatus()) || "1".equals(menuDto.getStatus()))) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.MENU_STATUS_ILLEGAL); } // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.MENU_NOT_EXIST_KEY + menuDto.getMenuId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, menuDto.getMenuId() + CommonMessageConstants.MENU_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysMenu::getMenuId, menuDto.getMenuId()); SysMenu menu = menuMapper.selectOne(queryWrapper); if (StringUtils.isNull(menu)) { redisCache.setCacheObject(CacheConstants.MENU_NOT_EXIST_KEY + menuDto.getMenuId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, menuDto.getMenuId() + CommonMessageConstants.MENU_NOT_EXIST); } menuMapper.updateMenuStatus(menuDto); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysMonitorServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.DateUtils; import ginyi.common.utils.NumberUtils; import ginyi.common.utils.StringUtils; import ginyi.common.utils.ip.IpUtils; import ginyi.system.domain.*; import ginyi.system.domain.model.dto.CacheDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.CacheKeyVo; import ginyi.system.domain.model.vo.CacheVo; import ginyi.system.service.ISysMonitorService; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.connection.DataType; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import oshi.SystemInfo; import oshi.hardware.CentralProcessor; import oshi.hardware.HardwareAbstractionLayer; import oshi.software.os.FileSystem; import oshi.software.os.OSFileStore; import javax.annotation.Resource; import java.lang.management.ManagementFactory; import java.util.*; @Slf4j @Service public class SysMonitorServiceImpl implements ISysMonitorService { @Resource private RedisCache redisCache; @Resource private RedisTemplate redisTemplate; /** * 获取系统服务信息 */ @Override public SysServer getServerInfo() throws InterruptedException { SysServer server = new SysServer(); SystemInfo systemInfo = new SystemInfo(); HardwareAbstractionLayer hardware = systemInfo.getHardware(); // CPU信息 long[] prevTicks = hardware.getProcessor().getSystemCpuLoadTicks(); Thread.sleep(1000); long[] ticks = hardware.getProcessor().getSystemCpuLoadTicks(); long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; long softIrq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; long SysRate = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; long userRate = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; long waitRate = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; long IdleRate = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; long totalCpu = userRate + nice + SysRate + IdleRate + waitRate + irq + softIrq + steal; SysCpu sysCpu = new SysCpu(); sysCpu.setCpuNum(hardware.getProcessor().getLogicalProcessorCount()); sysCpu.setTotal(totalCpu); sysCpu.setSys(NumberUtils.round(((double) SysRate / totalCpu) * 100, 2)); sysCpu.setUsed(NumberUtils.round(((double) userRate / totalCpu) * 100, 2)); sysCpu.setWait(NumberUtils.round(((double) waitRate / totalCpu) * 100, 2)); sysCpu.setFree(NumberUtils.round(((double) IdleRate / totalCpu) * 100, 2)); // 内存 SysMemory sysMemory = new SysMemory(); sysMemory.setTotal(NumberUtils.round((double) hardware.getMemory().getTotal() / (1024 * 1024 * 1024), 2)); sysMemory.setUsed(NumberUtils.round((double) (hardware.getMemory().getTotal() - hardware.getMemory().getAvailable()) / (1024 * 1024 * 1024), 2)); sysMemory.setFree(NumberUtils.round((double) hardware.getMemory().getAvailable() / (1024 * 1024 * 1024), 2)); sysMemory.setUsage(NumberUtils.round((sysMemory.getUsed() / sysMemory.getTotal()) * 100, 2)); // Java虚拟机 Properties properties = System.getProperties(); SysJvm sysJvm = new SysJvm(); sysJvm.setTotal(NumberUtils.round((double) Runtime.getRuntime().totalMemory() / (1024 * 1024), 2)); sysJvm.setMax(NumberUtils.round((double) Runtime.getRuntime().maxMemory() / (1024 * 1024), 2)); sysJvm.setFree(NumberUtils.round((double) Runtime.getRuntime().freeMemory() / (1024 * 1024), 2)); sysJvm.setUsed(NumberUtils.round(sysJvm.getTotal() - sysJvm.getFree(), 2)); sysJvm.setUsage(NumberUtils.round(((sysJvm.getTotal() - sysJvm.getFree()) / sysJvm.getTotal()) * 100, 2)); sysJvm.setInputArgs(ManagementFactory.getRuntimeMXBean().getInputArguments().toString()); sysJvm.setName(ManagementFactory.getRuntimeMXBean().getVmName()); sysJvm.setRunTime(DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate())); sysJvm.setStartTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate())); sysJvm.setVersion(properties.getProperty("java.version")); sysJvm.setHome(properties.getProperty("java.home")); // 服务器信息 Sys sys = new Sys(); sys.setComputerName(IpUtils.getHostName()); sys.setComputerIp(IpUtils.getHostIp()); sys.setOsName(properties.getProperty("os.name")); sys.setOsArch(properties.getProperty("os.arch")); sys.setUserDir(properties.getProperty("user.dir")); // 磁盘 FileSystem fileSystem = systemInfo.getOperatingSystem().getFileSystem(); List fsArray = fileSystem.getFileStores(); List sysFiles = new LinkedList<>(); for (OSFileStore fs : fsArray) { long free = fs.getUsableSpace(); long total = fs.getTotalSpace(); long used = total - free; SysFile sysFile = new SysFile(); sysFile.setDirName(fs.getMount()); sysFile.setTypeName(fs.getName()); sysFile.setTotal(NumberUtils.round((double) total / (1024 * 1024 * 1024), 2)); sysFile.setFree(NumberUtils.round((double) free / (1024 * 1024 * 1024), 2)); sysFile.setUsed(NumberUtils.round((double) used / (1024 * 1024 * 1024), 2)); sysFile.setUsage(NumberUtils.round(((double) used / total) * 100, 2)); sysFiles.add(sysFile); } server.setCpu(sysCpu); server.setMemory(sysMemory); server.setJvm(sysJvm); server.setSys(sys); server.setFile(sysFiles); return server; } /** * 获取缓存列表 */ @Override public BaseVo getCacheList() { BaseVo baseVo = new BaseVo<>(); // 获取所有key Set keys = redisCache.getKeys("*"); ArrayList list = new ArrayList<>(); for (String key : keys) { CacheKeyVo keyVo = new CacheKeyVo(); DataType type = redisCache.getType(key); keyVo.setKey(key); keyVo.setType(type.code()); list.add(keyVo); } // 排序 list.sort(Comparator.comparing(CacheKeyVo::getKey)); baseVo.setList(list); baseVo.setCount(keys.size()); return baseVo; } /** * 获取缓存详情 */ @Override public CacheVo getCacheDetails(CacheDto cacheDto) { if (!redisCache.hasKey(cacheDto.getKey())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.REDIS_KEY_NOT_EXIST); } Object object = null; try { if ("string".equalsIgnoreCase(cacheDto.getType())) { object = redisCache.getCacheObject(cacheDto.getKey(), Object.class); } if ("list".equalsIgnoreCase(cacheDto.getType())) { object = redisCache.getCacheList(cacheDto.getKey(), Object.class); } } catch (Exception e) { throw new CommonException(StateCode.ERROR_BUSINESS, CommonMessageConstants.REDIS_VALUE_TYPE_NOT_MATCH); } if (StringUtils.isNull(object)) { throw new CommonException(StateCode.ERROR_BUSINESS, CommonMessageConstants.REDIS_VALUE_TYPE_NOT_MATCH); } CacheVo cacheVo = new CacheVo(); cacheVo.setKey(cacheDto.getKey()); cacheVo.setExpire(redisCache.getExpire(cacheDto.getKey())); cacheVo.setValue(object); return cacheVo; } /** * 删除缓存 */ @Override public void removeCache(String key) { if (!redisCache.hasKey(key)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.REDIS_KEY_NOT_EXIST); } redisCache.removeCacheObject(key); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysOperationLogServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.system.domain.SysLogOperation; import ginyi.system.service.ISysOperLogService; import org.springframework.stereotype.Service; import java.util.List; /** * 操作日志 服务层处理 * * @author ruoyi */ @Service public class SysOperationLogServiceImpl implements ISysOperLogService { /** * 新增操作日志 * * @param operLog 操作日志对象 */ @Override public void insertOperlog(SysLogOperation operLog) { // todo 待写~ } /** * 查询系统操作日志集合 * * @param operLog 操作日志对象 * @return 操作日志集合 */ @Override public List selectOperLogList(SysLogOperation operLog) { // todo 待写~ return null; } /** * 批量删除系统操作日志 * * @param operIds 需要删除的操作日志ID * @return 结果 */ @Override public int deleteOperLogByIds(Long[] operIds) { // todo 待写~ return 0; } /** * 查询操作日志详细 * * @param operId 操作ID * @return 操作日志对象 */ @Override public SysLogOperation selectOperLogById(Long operId) { // todo 待写~ return null; } /** * 清空操作日志 */ @Override public void cleanOperLog() { // todo 待写~ } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysPasswordServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.common.constant.CacheConstants; import ginyi.common.exception.UserPasswordNotMatchException; import ginyi.common.exception.UserPasswordRetryLimitExceedException; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.framework.security.context.AuthenticationContextHolder; import ginyi.framework.security.utils.SecurityUtils; import ginyi.system.domain.SysUser; import ginyi.system.service.ISysPasswordService; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; @Service public class SysPasswordServiceImpl implements ISysPasswordService { @Resource private RedisCache redisCache; @Value(value = "${user.password.maxRetryCount}") private int maxRetryCount; @Value(value = "${user.password.lockTime}") private int lockTime; public void validate(SysUser user) { Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); String username = usernamePasswordAuthenticationToken.getName(); String password = usernamePasswordAuthenticationToken.getCredentials().toString(); Integer retryCount = redisCache.getCacheObject(getCacheKey(username), Integer.class); if (retryCount == null) { retryCount = 0; } // 错误输入次数大于上限 if (retryCount >= maxRetryCount) { StringBuilder errorMessage = new StringBuilder(); errorMessage.append("密码输入错误").append(maxRetryCount).append("次,帐号被系统锁定").append(lockTime).append("分钟,请稍后再试!"); throw new UserPasswordRetryLimitExceedException(StateCode.ERROR_UNAUTHENTICATION, errorMessage); } // 密码不匹配 if (!matches(user, password)) { retryCount = retryCount + 1; redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); throw new UserPasswordNotMatchException(); } else { clearLoginRecordCache(username); } } public boolean matches(SysUser user, String rawPassword) { return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); } /** * 登录账户密码错误次数缓存键名 * * @param username 用户名 * @return 缓存键key */ private String getCacheKey(String username) { return CacheConstants.PWD_ERR_CNT_KEY + username; } public void clearLoginRecordCache(String loginName) { if (redisCache.hasKey(getCacheKey(loginName))) { redisCache.removeCacheObject(getCacheKey(loginName)); } } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysPermissionServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.system.domain.SysRole; import ginyi.system.domain.SysUser; import ginyi.system.service.ISysMenuService; import ginyi.system.service.ISysPermissionService; import ginyi.system.service.ISysRoleService; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.HashSet; import java.util.List; import java.util.Set; @Service public class SysPermissionServiceImpl implements ISysPermissionService { @Resource private ISysRoleService roleService; @Resource private ISysMenuService menuService; /** * 获取角色数据权限 * * @param user 用户信息 * @return 角色权限信息 */ @Override 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 菜单权限信息 */ @Override public Set getMenuPermission(SysUser user) { Set perms = new HashSet(); // 管理员拥有所有权限 if (user.isAdmin()) { perms.add("*:*:*"); } else { List roles = user.getRoles(); if (!roles.isEmpty() && roles.size() > 1) { // 多角色设置permissions属性,以便数据权限匹配权限 for (SysRole role : roles) { Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); role.setPermissions(rolePerms); perms.addAll(rolePerms); } } else { perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); } } return perms; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysPostServiceImpl.java ================================================ package ginyi.framework.security.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.mysql.MyPage; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import ginyi.system.domain.SysMenu; import ginyi.system.domain.SysPost; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.PostVo; import ginyi.system.mapper.SysPostMapper; import ginyi.system.service.ISysPostService; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; import java.util.Set; @Service public class SysPostServiceImpl implements ISysPostService { @Resource private SysPostMapper postMapper; @Resource private RedisCache redisCache; /** * 查询岗位列表 * * @param postDto * @param page * @param pageSize * @return */ @Override public BaseVo list(PostDto postDto, Long page, Long pageSize) { IPage list = postMapper.list(postDto, new MyPage(page, pageSize).getPage()); BaseVo baseVo = new BaseVo<>(); baseVo.setList(list.getRecords()); baseVo.setCount((int) list.getTotal()); return baseVo; } /** * 获取岗位详情 * * @param postId * @return */ @Override public PostVo getPostByPostId(Long postId) { // 检查缓存中是否存在空id if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } PostVo postVo = new PostVo(); // 检查缓存中是否存在 SysPost post = redisCache.getCacheObject(CacheConstants.POST_DETAILS_BY_POSTID_KEY + postId, SysPost.class); if (StringUtils.isNotNull(post)) { BeanUtils.copyProperties(post, postVo); return postVo; } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostId, postId); post = postMapper.selectOne(queryWrapper); if (StringUtils.isNull(post)) { redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } redisCache.setCacheObject(CacheConstants.POST_DETAILS_BY_POSTID_KEY + postId, post); BeanUtils.copyProperties(post, postVo); return postVo; } /** * 添加岗位 * * @param postDto */ @Override public void addPost(PostDto postDto) { if (redisCache.hasKey(CacheConstants.ROLE_NAME_USED_KEY + postDto.getPostName())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_NAME_USED); } if (redisCache.hasKey(CacheConstants.ROLE_CODE_USED_KEY + postDto.getPostCode())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_CODE_USED); } // 检查名称是否被使用 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostName, postDto.getPostName()); SysPost post = postMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(post)) { redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + postDto.getPostName(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_NAME_USED); } // 检查编码是否被使用 queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostCode, postDto.getPostCode()); post = postMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(post)) { redisCache.setCacheObject(CacheConstants.ROLE_CODE_USED_KEY + postDto.getPostCode(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_CODE_USED); } postMapper.insertPost(postDto); redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX); redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX); } /** * 更新岗位 * * @param postDto */ @Override public void updatePost(PostDto postDto) { // 检查缓存中是否存在空id if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postDto.getPostId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostId, postDto.getPostId()); SysPost post = postMapper.selectOne(queryWrapper); if (StringUtils.isNull(post)) { redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postDto.getPostId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST); } // 检查名称是否被使用 queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostName, postDto.getPostName()); post = postMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(post) && !post.getPostId().equals(postDto.getPostId())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_NAME_USED); } // 检查编码是否被使用 queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostCode, postDto.getPostCode()); post = postMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(post) && !post.getPostId().equals(postDto.getPostId())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.POST_CODE_USED); } postMapper.updatePost(postDto); redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX); } /** * 删除岗位 * * @param postId */ @Override public void removePostById(Long postId) { // 查看缓存中是否存在空id if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostId, postId); SysPost post = postMapper.selectOne(queryWrapper); if (StringUtils.isNull(post)) { redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } postMapper.deleteById(postId); redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX); } /** * 批量删除岗位 * * @param ids */ @Override public void removeDeptByIds(Set ids) { if (ids.size() > 0) { List postList; postList = redisCache.getCacheList(CacheConstants.POST_LIST_KEY, SysPost.class); if (StringUtils.isNull(postList) || postList.size() == 0) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); postList = postMapper.selectList(queryWrapper); redisCache.setCacheList(CacheConstants.POST_LIST_KEY, postList); } for (Long postId : ids) { // 检查缓存中是否有标记着空id if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } boolean exist = postList.stream().anyMatch(post -> postId.equals(post.getPostId())); if (!exist) { redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } } postMapper.deleteBatchIds(ids); redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX); } else { throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } } /** * 更新状态 * @param postDto */ @Override public void updateStatus(PostDto postDto) { if (StringUtils.isNull(postDto.getPostId())) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.POST_ID_NOT_FOUND); } // 状态参数是否合法 if (!("0".equals(postDto.getStatus()) || "1".equals(postDto.getStatus()))) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.POST_STATUS_ILLEGAL); } // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + postDto.getPostId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysPost::getPostId, postDto.getPostId()); SysPost post = postMapper.selectOne(queryWrapper); if (StringUtils.isNull(post)) { redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postDto.getPostId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, postDto.getPostId() + CommonMessageConstants.POST_NOT_EXIST); } postMapper.updatePostStatus(postDto); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysRoleServiceImpl.java ================================================ package ginyi.framework.security.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.mysql.MyPage; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import ginyi.system.domain.SysMenu; import ginyi.system.domain.SysRole; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.RoleVo; import ginyi.system.mapper.SysMenuMapper; import ginyi.system.mapper.SysRoleMapper; import ginyi.system.service.ISysRoleService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @Service public class SysRoleServiceImpl implements ISysRoleService { @Resource private SysRoleMapper roleMapper; @Resource private SysMenuMapper menuMapper; @Resource private RedisCache redisCache; /** * 根据用户ID查询权限 * * @param userId 用户ID * @return 权限列表 */ @Override public Set selectRolePermissionByUserId(Long userId) { List perms = roleMapper.selectRolePermissionByUserId(userId); Set permsSet = new HashSet<>(); for (SysRole perm : perms) { if (StringUtils.isNotNull(perm)) { permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); } } return permsSet; } /** * 角色列表 * * @param roleDto * @param page * @param pageSize * @return */ @Override public BaseVo list(RoleDto roleDto, Long page, Long pageSize) { IPage list = roleMapper.list(roleDto, new MyPage(page, pageSize).getPage()); BaseVo baseVo = new BaseVo<>(); baseVo.setList(list.getRecords()); baseVo.setCount((int) list.getTotal()); return baseVo; } /** * 根据角色id获取角色 * * @param roleId * @return */ @Override public RoleVo getRoleByRoleId(Long roleId) { // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } // 检查缓存中是否存在 RoleVo role = redisCache.getCacheObject(CacheConstants.ROLE_DETAILS_BY_ROLEID_KEY + roleId, RoleVo.class); if (StringUtils.isNotNull(role)) { return role; } role = roleMapper.selectRoleByRoleId(roleId); if (StringUtils.isNull(role)) { redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } redisCache.setCacheObject(CacheConstants.ROLE_DETAILS_BY_ROLEID_KEY + roleId, role); return role; } /** * 新增角色 * * @param roleDto */ @Override @Transactional public void addRole(RoleDto roleDto) { // 角色名称是否被使用 if (redisCache.hasKey(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED); } // 角色权限字符是否被使用 if (redisCache.hasKey(CacheConstants.ROLE_CODE_USED_KEY + roleDto.getRoleName())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRole::getRoleName, roleDto.getRoleName()); SysRole role = roleMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(role)) { redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED); } queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRole::getRoleKey, roleDto.getRoleKey()); role = roleMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(role)) { redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleKey(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED); } roleMapper.insertRole(roleDto); roleMapper.insertRoleMenu(roleDto); redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX); } /** * 更新角色 * * @param roleDto */ @Override @Transactional public void updateRole(RoleDto roleDto) { // 检查缓存中是否存在空id if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST); } // 检查缓存中标记着角色权限字符已被使用 if (redisCache.hasKey(CacheConstants.ROLE_CODE_USED_KEY + roleDto.getRoleKey())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED); } // 检查缓存中标记着角色名称已被使用 if (redisCache.hasKey(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName())) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRole::getRoleId, roleDto.getRoleId()); SysRole role = roleMapper.selectOne(queryWrapper); // 是否存在该角色 if (StringUtils.isNull(role)) { redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST); } // 角色名称是否被使用 queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRole::getRoleName, roleDto.getRoleName()); role = roleMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(role) && !roleDto.getRoleId().equals(role.getRoleId())) { redisCache.setCacheObject(CacheConstants.ROLE_NAME_USED_KEY + roleDto.getRoleName(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_NAME_USED); } // 角色权限字符是否被使用 queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRole::getRoleKey, roleDto.getRoleKey()); role = roleMapper.selectOne(queryWrapper); if (StringUtils.isNotNull(role) && !roleDto.getRoleId().equals(role.getRoleId())) { redisCache.setCacheObject(CacheConstants.ROLE_CODE_USED_KEY + roleDto.getRoleKey(), null); throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.ROLE_PERMISSION_CODE_USED); } // 菜单id是否存在 if (roleDto.getPermissions().size() > 0) { List menuList = redisCache.getCacheList(CacheConstants.MENU_LIST_KEY, SysMenu.class); if (StringUtils.isNull(menuList) || menuList.size() == 0) { LambdaQueryWrapper menuQueryWrapper = new LambdaQueryWrapper<>(); menuList = menuMapper.selectList(menuQueryWrapper); redisCache.setCacheList(CacheConstants.MENU_LIST_KEY, menuList); } for (Long menuId : roleDto.getPermissions()) { boolean exist = menuList.stream().anyMatch(menu -> menuId.equals(menu.getMenuId())); if (!exist) { // 菜单id不存在 throw new CommonException(StateCode.ERROR_NOT_EXIST, menuId + CommonMessageConstants.ROLE_MENU_NOT_EXIST); } } } roleMapper.updateRole(roleDto); if (roleDto.getPermissions().size() > 0) { roleMapper.updateRoleMenu(roleDto); } redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX); } /** * 删除角色 * * @param roleId */ @Override public void removeByRoleId(Long roleId) { // 检查缓存中是否存在空id if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRole::getRoleId, roleId); SysRole role = roleMapper.selectOne(queryWrapper); if (StringUtils.isNull(role)) { redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } roleMapper.deleteById(roleId); redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX); } /** * 批量删除角色 * * @param ids */ @Override public void removeByRoleIds(Set ids) { if (ids.size() > 0) { List roleList; roleList = redisCache.getCacheList(CacheConstants.ROLE_LIST_KEY, SysRole.class); if (StringUtils.isNull(roleList) || roleList.size() == 0) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); roleList = roleMapper.selectList(queryWrapper); redisCache.setCacheList(CacheConstants.ROLE_LIST_KEY, roleList); } for (Long roleId : ids) { // 检查缓存中是否存在空id if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } boolean exist = roleList.stream().anyMatch(role -> roleId.equals(role.getRoleId())); if (!exist) { redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } } roleMapper.deleteBatchIds(ids); redisCache.removeCacheObject(CacheConstants.ROLE_KEY_PREFIX); } else { throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } } /** * 更新状态 * @param roleDto */ @Override public void updateStatus(RoleDto roleDto) { if (StringUtils.isNull(roleDto.getRoleId())) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.ROLE_ID_NOT_FOUND); } // 状态参数是否合法 if (!("0".equals(roleDto.getStatus()) || "1".equals(roleDto.getStatus()))) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.ROLE_STATUS_ILLEGAL); } // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysRole::getRoleId, roleDto.getRoleId()); SysRole menu = roleMapper.selectOne(queryWrapper); if (StringUtils.isNull(menu)) { redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleDto.getRoleId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, roleDto.getRoleId() + CommonMessageConstants.ROLE_NOT_EXIST); } roleMapper.updateRoleStatus(roleDto); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysUserServiceImpl.java ================================================ package ginyi.framework.security.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.constant.UserConstants; import ginyi.common.exception.CommonException; import ginyi.common.mysql.MyPage; import ginyi.common.redis.cache.RedisCache; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import ginyi.system.domain.SysDept; import ginyi.system.domain.SysPost; import ginyi.system.domain.SysRole; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.DeptDto; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.UserVo; import ginyi.system.mapper.SysDeptMapper; import ginyi.system.mapper.SysPostMapper; import ginyi.system.mapper.SysRoleMapper; import ginyi.system.mapper.SysUserMapper; import ginyi.system.service.ISysUserService; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @Service public class SysUserServiceImpl implements ISysUserService { @Resource private SysUserMapper userMapper; @Resource private SysPostMapper postMapper; @Resource private SysRoleMapper roleMapper; @Resource private SysDeptMapper deptMapper; @Resource private RedisCache redisCache; /** * 修改用户基本信息 * * @param userDto 用户信息 * @return 结果 */ @Override @Transactional public void updateUser(UserDto userDto) { if (checkLogic(userDto)) { userMapper.updateUser(userDto); if (StringUtils.isNotNull(userDto.getPostIds()) && userDto.getPostIds().size() > 0) { userMapper.updateUserPostIds(userDto); } if (StringUtils.isNotNull(userDto.getRoleIds()) && userDto.getRoleIds().size() > 0) { userMapper.updateUserRoleIds(userDto); } // 清除缓存 redisCache.removeCacheObject(CacheConstants.USER_KEY_PREFIX); } } /** * 新增用户 * * @param userDto */ @Override @Transactional public void addUser(UserDto userDto) { if (checkLogic(userDto)) { userMapper.insertUser(userDto); if (StringUtils.isNotNull(userDto.getPostIds()) && userDto.getPostIds().size() > 0) { userMapper.insertUserPostIds(userDto); } if (StringUtils.isNotNull(userDto.getRoleIds()) && userDto.getRoleIds().size() > 0) { userMapper.insertUserRoleIds(userDto); } // 清除缓存 redisCache.removeCacheObject(CacheConstants.USER_KEY_PREFIX); redisCache.removeCacheObject(CacheConstants.POST_KEY_PREFIX); redisCache.removeCacheObject(CacheConstants.DEPT_KEY_PREFIX); } } /** * 根据用户id查询用户 * * @param userId */ @Override public UserVo getUserByUserId(String userId) { // 检查缓存中是否记录着空的userId if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.USER_NOT_EXIST); } UserVo user; // 检查缓存中是否有该user user = redisCache.getCacheObject(CacheConstants.USER_DETAILS_BY_USERID_KEY + userId, UserVo.class); if (StringUtils.isNotNull(user)) { return user; } user = userMapper.selectUserByUserId(userId); if (StringUtils.isNull(user)) { redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.USER_NOT_EXIST); } // 存入缓存 redisCache.setCacheObject(CacheConstants.USER_DETAILS_BY_USERID_KEY + userId, user); return user; } /** * 获取用户列表(不含admin) * * @param userDto * @param page * @param pageSize */ @Override public BaseVo list(UserDto userDto, Long page, Long pageSize) { IPage list = userMapper.list(userDto, new MyPage(page, pageSize).getPage()); BaseVo baseVo = new BaseVo<>(); baseVo.setList(list.getRecords()); baseVo.setCount((int) list.getTotal()); return baseVo; } /** * 根据用户id删除用户 * * @param userId */ @Override public void removeById(Long userId) { // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysUser::getUserId, userId); SysUser user = userMapper.selectOne(queryWrapper); if (StringUtils.isNull(user)) { redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST); } userMapper.deleteById(userId); } /** * 批量删除用户 * * @param ids */ @Override @Transactional public void removeUserByIds(Set ids) { if (ids.size() > 0) { List userList; userList = redisCache.getCacheList(CacheConstants.USER_LIST_KEY, SysUser.class); if (StringUtils.isNull(userList) || userList.size() == 0) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); userList = userMapper.selectList(queryWrapper); redisCache.setCacheList(CacheConstants.USER_LIST_KEY, userList); } for (Long userId : ids) { // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST); } boolean exist = userList.stream().anyMatch(user -> userId.equals(user.getUserId())); if (!exist) { redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, userId + CommonMessageConstants.USER_NOT_EXIST); } } userMapper.deleteBatchIds(ids); redisCache.removeCacheObject(CacheConstants.USER_KEY_PREFIX); } else { throw new CommonException(StateCode.ERROR_REQUEST_PARAMS, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } } /** * 更改用户状态(0正常 1停用) * * @param userDto */ @Override public void updateStatus(UserDto userDto) { if (StringUtils.isNull(userDto.getUserId())) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.USER_ID_NOT_FOUND); } // 状态参数是否合法 if (!("0".equals(userDto.getStatus()) || "1".equals(userDto.getStatus()))) { throw new CommonException(StateCode.ERROR_PARAMS, CommonMessageConstants.USER_STATUS_ILLEGAL); } // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.USER_NOT_EXIST_KEY + userDto.getUserId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getUserId() + CommonMessageConstants.USER_NOT_EXIST); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysUser::getUserId, userDto.getUserId()); SysUser user = userMapper.selectOne(queryWrapper); if (StringUtils.isNull(user)) { redisCache.setCacheObject(CacheConstants.USER_NOT_EXIST_KEY + userDto.getUserId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getUserId() + CommonMessageConstants.USER_NOT_EXIST); } userMapper.updateUserStatus(userDto); } /** * 根据部门 id 获取用户列表 * * @param deptDto * @return */ @Override public BaseVo> getUserListByDeptIds(DeptDto deptDto) { List deptIds = deptDto.getDeptIds(); if (deptIds.size() == 0) { throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } List deptList = deptMapper.selectList(null); // 判断部门id是否合法 for (Long deptId : deptIds) { boolean isExist = false; for (SysDept dept : deptList) { if (deptId.equals(dept.getDeptId())) { isExist = true; break; } } if (!isExist) { throw new CommonException(StateCode.ERROR_NOT_EXIST, deptId + CommonMessageConstants.DEPT_NOT_EXIST); } } List userList = userMapper.selectUserByDeptIds(deptIds); ArrayList> arrayList = new ArrayList<>(); for (SysUser user : userList) { HashMap map = new HashMap<>(); map.put("userId", user.getUserId()); map.put("nickName", user.getNickName()); arrayList.add(map); } BaseVo> baseVo = new BaseVo<>(); baseVo.setList(arrayList); baseVo.setCount(arrayList.size()); return baseVo; } /** * 根据岗位id获取用户列表 * @param postDto * @return */ @Override public BaseVo> getUserListByPostIds(PostDto postDto) { List postIds = postDto.getPostIds(); if (postIds.size() == 0) { throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } List postList = postMapper.selectList(null); // 判断岗位id是否合法 for (Long postId : postIds) { boolean isExist = false; for (SysPost post : postList) { if (postId.equals(post.getPostId())) { isExist = true; break; } } if (!isExist) { throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } } // 查出所有用户 IPage userList = userMapper.list(new UserDto(), new MyPage(null, null).getPage()); ArrayList> arrayList = new ArrayList<>(); for (Long postId : postDto.getPostIds()) { for (UserVo user : userList.getRecords()) { HashMap map = new HashMap<>(); if (user.getPostIds().contains(postId)) { map.put("userId", user.getUserId()); map.put("nickName", user.getNickName()); arrayList.add(map); } } } // 去重 List> resultList = arrayList.stream().distinct().collect(Collectors.toList()); BaseVo> baseVo = new BaseVo<>(); baseVo.setList(resultList); baseVo.setCount(resultList.size()); return baseVo; } /** * 根据角色 id 获取用户列表 * @param roleDto * @return */ @Override public BaseVo> getUserListByRoleIds(RoleDto roleDto) { List roleIds = roleDto.getRoleIds(); if (roleIds.size() == 0) { throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.SYS_REQUEST_ILLEGAL); } List roleList = roleMapper.selectList(null); // 判断岗位id是否合法 for (Long roleId : roleIds) { boolean isExist = false; for (SysRole role : roleList) { if (roleId.equals(role.getRoleId())) { isExist = true; break; } } if (!isExist) { throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } } // 查出所有用户 IPage userList = userMapper.list(new UserDto(), new MyPage(null, null).getPage()); ArrayList> arrayList = new ArrayList<>(); for (Long roleId : roleDto.getRoleIds()) { for (UserVo user : userList.getRecords()) { HashMap map = new HashMap<>(); if (user.getRoleIds().contains(roleId)) { map.put("userId", user.getUserId()); map.put("nickName", user.getNickName()); arrayList.add(map); } } } // 去重 List> resultList = arrayList.stream().distinct().collect(Collectors.toList()); BaseVo> baseVo = new BaseVo<>(); baseVo.setList(resultList); baseVo.setCount(resultList.size()); return baseVo; } /** * 校验逻辑 * * @param userDto */ public boolean checkLogic(UserDto userDto) { // 判断部门是否存在 if (StringUtils.isNotNull(userDto.getDeptId())) { // 检查缓存中是否标记着空id if (redisCache.hasKey(CacheConstants.DEPT_NOT_EXIST_KEY + userDto.getDeptId())) { throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST); } SysDept sysDept; LambdaQueryWrapper deptQueryWrapper = new LambdaQueryWrapper<>(); deptQueryWrapper.eq(SysDept::getDeptId, userDto.getDeptId()); sysDept = deptMapper.selectOne(deptQueryWrapper); if (sysDept == null) { redisCache.setCacheObject(CacheConstants.DEPT_NOT_EXIST_KEY + userDto.getDeptId(), null); throw new CommonException(StateCode.ERROR_NOT_EXIST, userDto.getDeptId() + CommonMessageConstants.DEPT_NOT_EXIST); } } // 用户名是否被占用(插入时) if (StringUtils.isNotBlank(userDto.getUserName()) && StringUtils.isNull(userDto.getUserId())) { LambdaQueryWrapper userQueryWrapper = new LambdaQueryWrapper<>(); userQueryWrapper.eq(SysUser::getUserName, userDto.getUserName()); SysUser user = userMapper.selectOne(userQueryWrapper); if (user != null) { throw new CommonException(StateCode.ERROR_EXIST, CommonMessageConstants.USER_NAME_USED); } } // 判断插入的岗位id是否存在 if (StringUtils.isNotNull(userDto.getPostIds()) && userDto.getPostIds().size() > 0) { SysPost sysPost; for (Long postId : userDto.getPostIds()) { if (redisCache.hasKey(CacheConstants.POST_NOT_EXIST_KEY + postId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } LambdaQueryWrapper postQueryWrapper = new LambdaQueryWrapper<>(); postQueryWrapper.eq(SysPost::getPostId, postId); sysPost = postMapper.selectOne(postQueryWrapper); if (sysPost == null) { redisCache.setCacheObject(CacheConstants.POST_NOT_EXIST_KEY + postId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, postId + CommonMessageConstants.POST_NOT_EXIST); } } } // 判断插入的角色id是否存在 if (StringUtils.isNotNull(userDto.getRoleIds()) && userDto.getRoleIds().size() > 0) { SysRole sysRole; for (Long roleId : userDto.getRoleIds()) { if (redisCache.hasKey(CacheConstants.ROLE_NOT_EXIST_KEY + roleId)) { throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } LambdaQueryWrapper roleQueryWrapper = new LambdaQueryWrapper<>(); roleQueryWrapper.eq(SysRole::getRoleId, roleId); sysRole = roleMapper.selectOne(roleQueryWrapper); if (sysRole == null) { redisCache.setCacheObject(CacheConstants.ROLE_NOT_EXIST_KEY + roleId, null); throw new CommonException(StateCode.ERROR_NOT_EXIST, roleId + CommonMessageConstants.ROLE_NOT_EXIST); } } } return true; } /** * 注册用户信息 * * @param user 用户信息 * @return 结果 */ @Override public boolean registerUser(SysUser user) { UserDto userDto = new UserDto(); BeanUtils.copyProperties(user, userDto); return userMapper.insertUser(userDto) > 0; } @Override public SysUser selectUserByUserName(String userName) { return userMapper.selectUserByUserName(userName); } /** * 校验用户名称是否唯一 * * @param user 用户信息 * @return 结果 */ @Override public String checkUserNameUnique(SysUser user) { Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); SysUser info = userMapper.checkUserNameUnique(user.getUserName()); if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { return UserConstants.NOT_UNIQUE; } return UserConstants.UNIQUE; } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/SysVerifyServiceImpl.java ================================================ package ginyi.framework.security.service; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.CircleCaptcha; import cn.hutool.crypto.SecureUtil; import cn.hutool.extra.servlet.ServletUtil; import ginyi.common.constant.CacheConstants; import ginyi.common.exception.CommonException; import ginyi.common.redis.cache.RedisCache; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.result.StateCode; import ginyi.common.utils.Constants; import ginyi.common.utils.StringUtils; import ginyi.system.service.ISysConfigService; import ginyi.system.service.IVerifyService; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.concurrent.TimeUnit; @Service public class SysVerifyServiceImpl implements IVerifyService { @Resource private HttpServletRequest request; @Resource private RedisCache redisCache; @Resource private ISysConfigService configService; /** * 图片验证码 */ @Override public String captcha() { boolean captchaEnabled = configService.selectCaptchaEnabled(); if (!captchaEnabled) { return null; } CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 15); String img = captcha.getImageBase64(); String code = captcha.getCode(); String clientIP = ServletUtil.getClientIP(request); String userAgent = ServletUtil.getHeader(request, "User-Agent", "utf-8"); // 缓存key String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + SecureUtil.md5(clientIP + userAgent); redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); return img; } /** * 校验图片验证码 * * @param code */ @Override public void checkImgCode(String code) { String clientIP = ServletUtil.getClientIP(request); String userAgent = ServletUtil.getHeader(request, "User-Agent", "utf-8"); String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + SecureUtil.md5(clientIP + userAgent); String captcha = redisCache.getCacheObject(verifyKey, String.class); if(StringUtils.isNull(code)){ throw new CommonException(StateCode.ERROR_NOT_EXIST, CommonMessageConstants.SYS_CAPTCHA_NOT_EXIST); } // 验证码失效 if (captcha == null) { throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.VERIFY_EXPIRE); } if (!captcha.equalsIgnoreCase(code)) { throw new CommonException(StateCode.ERROR_PARAMS_SERVICE, CommonMessageConstants.VERiFY_INCORRECT); } else { // 输入正确,删除该验证码 redisCache.removeCacheObject(verifyKey); } } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/TokenServiceImpl.java ================================================ package ginyi.framework.security.service; import eu.bitwalker.useragentutils.UserAgent; import ginyi.common.constant.CacheConstants; import ginyi.common.constant.UserConstants; import ginyi.common.redis.cache.RedisCache; import ginyi.common.utils.ServletUtils; import ginyi.common.utils.StringUtils; import ginyi.common.utils.Constants; import ginyi.common.utils.ip.AddressUtils; import ginyi.common.utils.ip.IpUtils; import ginyi.common.utils.uuid.IdUtils; import ginyi.system.domain.LoginUser; import ginyi.system.service.ITokenService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @Slf4j @Service public class TokenServiceImpl implements ITokenService { @Resource private RedisCache redisCache; @Resource private HttpServletRequest request; /** * 令牌自定义标识 */ @Value("${token.header}") private String header; /** * 令牌秘钥 */ @Value("${token.secret}") private String secret; /** * 令牌有效期(默认30分钟) */ @Value("${token.expireTime}") private int expireTime; protected static final long MILLIS_SECOND = 1000; protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; /** * 从数据声明生成令牌 * * @param claims 数据声明 * @return 令牌 */ private String createToken(Map claims) { String token = Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, secret).compact(); return token; } /** * 创建令牌 * * @param loginUser 用户信息 * @return 令牌 */ @Override public String createToken(LoginUser loginUser) { String token = IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser); Map claims = new HashMap<>(); claims.put(Constants.LOGIN_USER_KEY, token); return createToken(claims); } /** * 获取用户身份信息 * * @return 用户信息 */ @Override public LoginUser getLoginUser(HttpServletRequest request) { // 获取请求携带的令牌 String token = getToken(request); if (StringUtils.isNotEmpty(token)) { try { Claims claims = parseToken(token); // 解析对应的权限以及用户信息 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); String userKey = getTokenKey(uuid); LoginUser user = redisCache.getCacheObject(userKey, LoginUser.class); request.setAttribute(UserConstants.CURRENT_USER, user.getUsername()); return user; } catch (Exception e) { } } return null; } /** * 删除用户身份信息 */ @Override public void delLoginUser(String token) { if (StringUtils.isNotEmpty(token)) { String userKey = getTokenKey(token); redisCache.removeCacheObject(userKey); } } /** * 验证令牌有效期,相差不足20分钟,自动刷新缓存 * * @param loginUser * @return 令牌 */ @Override public void verifyToken(LoginUser loginUser) { long expireTime = loginUser.getExpireTime(); long currentTime = System.currentTimeMillis(); if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { refreshToken(loginUser); } } /** * 设置用户代理信息 * * @param loginUser 登录信息 */ public void setUserAgent(LoginUser loginUser) { UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); loginUser.setIpaddr(ip); loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); loginUser.setBrowser(userAgent.getBrowser().getName()); loginUser.setOs(userAgent.getOperatingSystem().getName()); } /** * 刷新令牌有效期 * * @param loginUser 登录信息 */ public void refreshToken(LoginUser loginUser) { loginUser.setLoginTime(System.currentTimeMillis()); loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); // 根据uuid将loginUser缓存 String userKey = getTokenKey(loginUser.getToken()); redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); } private String getTokenKey(String uuid) { return CacheConstants.LOGIN_TOKEN_KEY + uuid; } /** * 获取请求token * * @param request * @return token */ private String getToken(HttpServletRequest request) { String token = request.getHeader(header); if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) { token = token.replace(Constants.TOKEN_PREFIX, ""); } return token; } /** * 从令牌中获取数据声明 * * @param token 令牌 * @return 数据声明 */ private Claims parseToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/service/UserDetailsServiceImpl.java ================================================ package ginyi.framework.security.service; import ginyi.common.exception.CommonException; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.result.StateCode; import ginyi.common.utils.StringUtils; import ginyi.common.utils.enums.UserStatus; import ginyi.system.domain.LoginUser; import ginyi.system.domain.SysUser; import ginyi.system.service.ISysPasswordService; import ginyi.system.service.ISysPermissionService; import ginyi.system.service.ISysUserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class UserDetailsServiceImpl implements UserDetailsService { private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); @Resource private ISysUserService userService; @Resource private ISysPasswordService passwordService; @Resource private ISysPermissionService permissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user = userService.selectUserByUserName(username); if (StringUtils.isNull(user)) { // 不存在 throw new CommonException(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_NOT_EXIST); } else if (UserStatus.DELETED.getCode().equals(user.getDeleted())) { // 被删除 throw new CommonException(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_IS_DELETED); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { // 被停用 throw new CommonException(StateCode.ERROR_UNAUTHENTICATION, CommonMessageConstants.USER_IS_FORBIDDEN); } passwordService.validate(user); return createLoginUser(user); } public UserDetails createLoginUser(SysUser user) { return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-security/src/main/java/ginyi/framework/security/utils/SecurityUtils.java ================================================ package ginyi.framework.security.utils; import ginyi.common.constant.CommonMessageConstants; import ginyi.common.exception.CommonException; import ginyi.common.result.StateCode; import ginyi.system.domain.LoginUser; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** * 安全服务工具类 * * @author ruoyi */ public class SecurityUtils { /** * 获取Authentication */ public static Authentication getAuthentication() { return SecurityContextHolder.getContext().getAuthentication(); } /** * 获取用户 **/ public static LoginUser getLoginUser() { try { return (LoginUser) getAuthentication().getPrincipal(); } catch (Exception e) { throw new CommonException(StateCode.ERROR_SYSTEM, CommonMessageConstants.USER_NOT_FOUND); } } /** * 判断密码是否相同 * * @param rawPassword 真实密码 * @param encodedPassword 加密后字符 * @return 结果 */ public static boolean matchesPassword(String rawPassword, String encodedPassword) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder.matches(rawPassword, encodedPassword); } /** * 生成BCryptPasswordEncoder密码 * * @param password 密码 * @return 加密字符串 */ public static String encryptPassword(String password) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder.encode(password); } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-websocket/pom.xml ================================================ ginyi-framework com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-framework-websocket org.springframework.boot spring-boot-starter-websocket ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-websocket/src/main/java/ginyi/framework/websocket/WebSocket.java ================================================ package ginyi.framework.websocket; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; @Slf4j @Component @ServerEndpoint(value = "/websocket/{userId}") // 接口路径 ws://主机ip:端口/webSocket; public class WebSocket { /** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** * 用户ID */ private String userId; /** * concurrent包的线程安全 Set,用来存放每个客户端对应的MyWebSocket对象。 * 虽然 @Component 默认是单例模式的,但 springboot 还是会为每个 websocket 连接初始化一个 bean,所以可以用一个静态 set 保存起来 * 注:底下WebSocket是当前类名 */ private static CopyOnWriteArraySet webSockets = new CopyOnWriteArraySet<>(); /** * 用来存在线连接用户信息 */ private static ConcurrentHashMap sessionPool = new ConcurrentHashMap<>(); /** * 链接成功调用的方法 */ @OnOpen public void onOpen(Session session, @PathParam(value = "userId") String userId) { try { this.session = session; this.userId = userId; webSockets.add(this); sessionPool.put(userId, session); log.info("【websocket消息】新的连接,新建连接的用户是:{}, 当前总连接数为:{}", userId, webSockets.size()); } catch (Exception e) { } } /** * 链接关闭调用的方法 */ @OnClose public void onClose() { try { webSockets.remove(this); sessionPool.remove(this.userId); log.info("【websocket消息】连接断开,断开连接的用户是:{},当前总连接数为:{}", userId, webSockets.size()); } catch (Exception e) { } } /** * 收到客户端消息后调用的方法 * * @param message */ @OnMessage public void onMessage(String message, @PathParam(value = "userId") String userId) { log.info("【websocket消息】收到客户端消息,发送者是:{},消息是:{}", userId, message); } /** * 发送错误时的处理 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { log.error("用户错误,原因:{}", error.getMessage()); error.printStackTrace(); } /** * 此为广播消息 * * @param message */ public void sendAllMessage(String message) { log.info("【websocket消息】广播消息:" + message); for (WebSocket webSocket : webSockets) { try { if (webSocket.session.isOpen()) { webSocket.session.getAsyncRemote().sendText(message); } } catch (Exception e) { e.printStackTrace(); } } } /** * 此为单点消息 (单人) * * @param userId * @param message */ public void sendOneMessage(String userId, String message) { Session session = sessionPool.get(userId); if (session != null && session.isOpen()) { try { log.info("【websocket消息】 单点消息:" + message); session.getAsyncRemote().sendText(message); } catch (Exception e) { e.printStackTrace(); } } } /** * 此为单点消息 (多人) * * @param userIds * @param message */ public void sendMoreMessage(String[] userIds, String message) { for (String userId : userIds) { Session session = sessionPool.get(userId); if (session != null && session.isOpen()) { try { log.info("【websocket消息】 单点消息:" + message); session.getAsyncRemote().sendText(message); } catch (Exception e) { e.printStackTrace(); } } } } } ================================================ FILE: ginyi-springboot/ginyi-framework/ginyi-framework-websocket/src/main/java/ginyi/framework/websocket/config/WebSocketConfig.java ================================================ package ginyi.framework.websocket.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { /** * 注入ServerEndpointExporter, * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ================================================ FILE: ginyi-springboot/ginyi-framework/pom.xml ================================================ ginyi-springboot com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-framework pom ginyi-framework-core ginyi-framework-security ginyi-framework-websocket com.ginyi ginyi-system ${ginyi.version} ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/Dockerfile ================================================ FROM java:8 COPY *.jar /app.jar EXPOSE 8066 ENTRYPOINT ["java","-jar","/app.jar"] ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/pom.xml ================================================ ginyi-server com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-server-admin io.springfox springfox-swagger2 io.springfox springfox-swagger-ui com.github.xiaoymin knife4j-spring-boot-starter src/main/resources **/*.* false src/main/java **/*.xml false org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/AdminApplication.java ================================================ package ginyi.server.admin; import ginyi.framework.core.config.AppConfig; import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import java.net.UnknownHostException; // 扫描系统服务的包以及自身所在模块的包 @SpringBootApplication(scanBasePackages = { "ginyi.system", "ginyi.common", "ginyi.framework.security", "ginyi.server.admin" }) @MapperScan({ "ginyi.system.**.mapper", "ginyi.server.admin.**.mapper" }) @Slf4j public class AdminApplication { public static void main(String[] args) throws UnknownHostException { ConfigurableApplicationContext context = SpringApplication.run(AdminApplication.class, args); AppConfig.printAppInfo(context); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysDeptController.java ================================================ package ginyi.server.admin.controller; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import ginyi.common.annotation.Log; import ginyi.common.enums.BusinessType; import ginyi.common.result.CommonResult; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.SysDept; import ginyi.system.domain.model.dto.DeptDto; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.DeptVo; import ginyi.system.service.ISysDeptService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Set; @Api(tags = "部门模块") @RestController @RequestMapping("/api/dept") public class SysDeptController { @Resource private ISysDeptService deptService; @ApiOperation("删除部门") @PostMapping("/delete/{deptId}") @PreAuthorize("@ss.hasPermission('system:dept:remove')") @Log(title = "部门模块", businessType = BusinessType.DELETE) public CommonResult delete(@PathVariable("deptId") Long deptId) { deptService.removeDeptById(deptId); return CommonResult.success(); } @ApiOperation("批量删除部门") @PostMapping("/delete") @PreAuthorize("@ss.hasPermission('system:dept:remove')") @Log(title = "部门模块", businessType = BusinessType.DELETE) public CommonResult delete(@RequestBody Set ids) { deptService.removeDeptByIds(ids); return CommonResult.success(); } @ApiOperation("部门详情") @GetMapping("/getDeptByDeptId/{deptId}") @PreAuthorize("@ss.hasPermission('system:dept:edit')") public CommonResult getDeptByDeptId(@PathVariable("deptId") Long deptId) { DeptVo deptVo = deptService.getDeptByDeptId(deptId); return CommonResult.success(deptVo); } @ApiOperation("部门列表") @PostMapping("/list") @PreAuthorize("@ss.hasPermission('system:dept:list')") @ApiOperationSupport(ignoreParameters = { "deptDto.updateBy", "deptDto.updateTime", "deptDto.createTime", "deptDto.params", "deptDto.ancestors", "deptDto.deptId", "deptDto.sort", }) public CommonResult> list(@RequestBody DeptDto deptDto, @RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo list = deptService.list(deptDto, page, pageSize); return CommonResult.success(list); } @ApiOperation("新增部门") @PostMapping("/add") @PreAuthorize("@ss.hasPermission('system:dept:add')") @Log(title = "部门模块", businessType = BusinessType.INSERT) @ApiOperationSupport(ignoreParameters = { "deptDto.updateBy", "deptDto.updateTime", "deptDto.createTime", "deptDto.createBy", "deptDto.params", "deptDto.ancestors", "deptDto.deptId", "deptDto.beginTime", "deptDto.endTime", }) public CommonResult addDept(@RequestBody @Validated(AddGroup.class) DeptDto deptDto) { deptService.addDept(deptDto); return CommonResult.success(); } @ApiOperation("更新部门") @PostMapping("/update") @PreAuthorize("@ss.hasPermission('system:dept:edit')") @Log(title = "部门模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(ignoreParameters = { "deptDto.updateBy", "deptDto.updateTime", "deptDto.createTime", "deptDto.createBy", "deptDto.params", "deptDto.ancestors", "deptDto.beginTime", "deptDto.endTime", }) public CommonResult update(@RequestBody @Validated(UpdateGroup.class) DeptDto deptDto) { deptService.updateDept(deptDto); return CommonResult.success(); } @ApiOperation("更新部门状态") @PostMapping("/updateStatus") @PreAuthorize("@ss.hasPermission('system:dept:edit')") @Log(title = "部门模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(includeParameters = { "deptDto.deptId", "deptDto.status" }) public CommonResult updateStatus(@RequestBody DeptDto deptDto){ deptService.updateStatus(deptDto); return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysLogController.java ================================================ package ginyi.server.admin.controller; import ginyi.common.result.CommonResult; import ginyi.system.domain.SysLogLogin; import ginyi.system.domain.SysLogOperation; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.service.ISysLogService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @Api(tags = "日志模块") @RestController @RequestMapping("/api/log") public class SysLogController { @Resource private ISysLogService logService; @ApiOperation("登录日志") @GetMapping("/getLoginLogList") @PreAuthorize("@ss.hasPermission('monitor:operlog:list')") public CommonResult> loginLogList(@RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo list = logService.getLoginLogList(page, pageSize); return CommonResult.success(list); } @ApiOperation("操作日志") @GetMapping("/getOperationLogList") @PreAuthorize("@ss.hasPermission('monitor:loginlog:list')") public CommonResult> operationLogList(@RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo list = logService.getOperationLogList(page, pageSize); return CommonResult.success(list); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysLoginController.java ================================================ package ginyi.server.admin.controller; import ginyi.common.result.CommonResult; import ginyi.system.domain.model.dto.LoginDto; import ginyi.system.domain.model.dto.RegisterDto; import ginyi.system.domain.model.vo.LoginVo; import ginyi.system.service.ISysLoginService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/user") @Api(tags = "登录模块") @Slf4j public class SysLoginController { @Autowired private ISysLoginService loginService; @ApiOperation("用户登录") @PostMapping("/login") public CommonResult login(@RequestBody @Validated LoginDto loginDto) { LoginVo loginVo = loginService.login(loginDto); return CommonResult.success(loginVo); } @ApiOperation("用户注册") @PostMapping("/register") public CommonResult register(@RequestBody @Validated RegisterDto registerDto) { loginService.register(registerDto); return CommonResult.success(); } @ApiOperation("退出登录") @PostMapping("/logout") public CommonResult logout() { return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysMenuController.java ================================================ package ginyi.server.admin.controller; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import ginyi.common.annotation.Log; import ginyi.common.enums.BusinessType; import ginyi.common.result.CommonResult; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.SysMenu; import ginyi.system.domain.model.dto.MenuDto; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.service.ISysMenuService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Set; @Api(tags = "菜单模块") @RestController @RequestMapping("/api/menu") public class SysMenuController { @Resource private ISysMenuService menuService; @ApiOperation("删除菜单") @PostMapping("/delete/{menuId}") @PreAuthorize("@ss.hasPermission('system:menu:remove')") @Log(title = "菜单模块", businessType = BusinessType.DELETE) public CommonResult delete(@PathVariable("menuId") Long menuId) { menuService.removeMenuById(menuId); return CommonResult.success(); } @ApiOperation("批量删除菜单") @PostMapping("/delete") @PreAuthorize("@ss.hasPermission('system:menu:remove')") @Log(title = "菜单模块", businessType = BusinessType.DELETE) public CommonResult delete(@RequestBody Set ids) { menuService.removeMenuByIds(ids); return CommonResult.success(); } @ApiOperation("菜单详情") @GetMapping("/getMenuById/{menuId}") @PreAuthorize("@ss.hasPermission('system:menu:edit')") public CommonResult getMenuById(@PathVariable("menuId") Long menuId) { SysMenu menu = menuService.getMenuById(menuId); return CommonResult.success(menu); } @ApiOperation("路由菜单列表") @GetMapping("/getRouterList") public CommonResult> list() { BaseVo baseVo = menuService.selectMenuList(); return CommonResult.success(baseVo); } @ApiOperation("菜单列表") @PostMapping("/list") @PreAuthorize("@ss.hasPermission('system:menu:list')") @ApiOperationSupport(ignoreParameters = { "menuDto.updateBy", "menuDto.updateTime", "menuDto.createTime", "menuDto.params", }) public CommonResult> list(@RequestBody @Validated MenuDto menuDto) { BaseVo baseVo = menuService.list(menuDto); return CommonResult.success(baseVo); } @ApiOperation("新增菜单") @PostMapping("/add") @Log(title = "菜单模块", businessType = BusinessType.INSERT) @PreAuthorize("@ss.hasPermission('system:menu:add')") @ApiOperationSupport(ignoreParameters = { "menuDto.updateBy", "menuDto.updateTime", "menuDto.createTime", "menuDto.createBy", "menuDto.params", "menuDto.menuId", }) public CommonResult add(@RequestBody @Validated({AddGroup.class}) MenuDto menuDto) { menuService.addMenu(menuDto); return CommonResult.success(); } @ApiOperation("更新菜单") @PostMapping("/update") @PreAuthorize("@ss.hasPermission('system:menu:edit')") @Log(title = "菜单模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(ignoreParameters = { "menuDto.updateBy", "menuDto.updateTime", "menuDto.createTime", "menuDto.createBy", "menuDto.params", }) public CommonResult update(@RequestBody @Validated({UpdateGroup.class}) MenuDto menuDto) { menuService.updateMenu(menuDto); return CommonResult.success(); } @ApiOperation("更新菜单状态") @PostMapping("/updateStatus") @PreAuthorize("@ss.hasPermission('system:menu:edit')") @Log(title = "菜单模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(includeParameters = { "menuDto.menuId", "menuDto.status" }) public CommonResult updateStatus(@RequestBody MenuDto menuDto){ menuService.updateStatus(menuDto); return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysMonitorController.java ================================================ package ginyi.server.admin.controller; import ginyi.common.annotation.Log; import ginyi.common.result.CommonResult; import ginyi.system.domain.SysServer; import ginyi.system.domain.model.dto.CacheDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.CacheKeyVo; import ginyi.system.domain.model.vo.CacheVo; import ginyi.system.service.ISysMonitorService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @Api(tags = "监控模块") @RestController @RequestMapping("/api/monitor") public class SysMonitorController { @Resource private ISysMonitorService monitorService; @ApiOperation("获取系统服务信息") @GetMapping("/getServerInfo") @PreAuthorize("@ss.hasPermission('monitor:server:list')") public CommonResult getServerInfo() throws InterruptedException { SysServer server = monitorService.getServerInfo(); return CommonResult.success(server); } @ApiOperation("获取缓存列表") @GetMapping("/getCacheList") @PreAuthorize("@ss.hasPermission('monitor:cache:list')") public CommonResult> getCacheList(){ BaseVo baseVo = monitorService.getCacheList(); return CommonResult.success(baseVo); } @ApiOperation("获取缓存详情") @PostMapping("/getCacheValue") @PreAuthorize("@ss.hasPermission('monitor:cache:details')") public CommonResult getCacheDetails(@RequestBody CacheDto cacheDto){ CacheVo cacheVo = monitorService.getCacheDetails(cacheDto); return CommonResult.success(cacheVo); } @ApiOperation("删除缓存") @PostMapping("/removeCache/{key}") @Log(title = "缓存模块") @PreAuthorize("@ss.hasPermission('monitor:cache:remove')") public CommonResult removeCache(@PathVariable String key){ monitorService.removeCache(key); return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysNoticeController.java ================================================ package ginyi.server.admin.controller; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import ginyi.common.annotation.Log; import ginyi.common.enums.BusinessType; import ginyi.common.result.CommonResult; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.SysNotice; import ginyi.system.domain.model.dto.NoticeDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.NoticeVo; import ginyi.system.service.ISysNoticeService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @Api(tags = "通知公告模块") @RestController @RequestMapping("/api/notice") public class SysNoticeController { @Resource private ISysNoticeService noticeService; @ApiOperation("通知公告列表(管理员)") @PostMapping("/list") @ApiOperationSupport(ignoreParameters = { "noticeDto.noticeId", "noticeDto.remark", "noticeDto.updateBy", "noticeDto.updateTime", "noticeDto.createBy", "noticeDto.createTime", "noticeDto.params", "noticeDto.content", "noticeDto.userIds", "noticeDto.userReadIds", }) @PreAuthorize("@ss.hasPermission('system:notice:query:admin')") public CommonResult> list(@RequestBody NoticeDto noticeDto, @RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo baseVo = noticeService.list(noticeDto, page, pageSize); return CommonResult.success(baseVo); } @ApiOperation("获取用户通知公告列表") @GetMapping("/getUserNoticeList") @PreAuthorize("@ss.hasPermission('system:notice:query')") public CommonResult> getUserNoticeList(@RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize){ BaseVo baseVo = noticeService.getUserNoticeList(page, pageSize); return CommonResult.success(baseVo); } @ApiOperation("发布通知公告") @PostMapping("/add") @ApiOperationSupport(ignoreParameters = { "noticeDto.noticeId", "noticeDto.remark", "noticeDto.updateBy", "noticeDto.updateTime", "noticeDto.createBy", "noticeDto.createTime", "noticeDto.params", "noticeDto.userReadIds", }) @PreAuthorize("@ss.hasPermission('system:notice:add')") @Log(title = "通知公告模块", businessType = BusinessType.INSERT) public CommonResult add(@RequestBody @Validated(AddGroup.class) NoticeDto noticeDto){ noticeService.add(noticeDto); return CommonResult.success(); } @ApiOperation("更新通知公告") @PostMapping("/update") @ApiOperationSupport(ignoreParameters = { "noticeDto.remark", "noticeDto.updateBy", "noticeDto.updateTime", "noticeDto.createBy", "noticeDto.createTime", "noticeDto.params", "noticeDto.userReadIds", }) @PreAuthorize("@ss.hasPermission('system:notice:edit')") @Log(title = "通知公告模块", businessType = BusinessType.UPDATE) public CommonResult update(@RequestBody @Validated(UpdateGroup.class) NoticeDto noticeDto){ noticeService.updateNotice(noticeDto); return CommonResult.success(); } @ApiOperation("确认收到通知公告") @PostMapping("/haveRead/{noticeId}") @PreAuthorize("@ss.hasPermission('system:notice:haveRead')") @Log(title = "通知公告模块", businessType = BusinessType.UPDATE) public CommonResult haveRead(@PathVariable Long noticeId){ noticeService.haveRead(noticeId); return CommonResult.success(); } @ApiOperation("删除通知公告") @PostMapping("/delete/{noticeId}") @PreAuthorize("@ss.hasPermission('system:notice:remove')") @Log(title = "通知公告模块", businessType = BusinessType.DELETE) public CommonResult remove(@PathVariable Long noticeId){ noticeService.remove(noticeId); return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysOnlineController.java ================================================ package ginyi.server.admin.controller; import ginyi.common.annotation.Log; import ginyi.common.enums.BusinessType; import ginyi.common.result.CommonResult; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.SessionUserVo; import ginyi.system.service.ISysOnlineService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Set; @Api(tags = "在线用户") @RestController @RequestMapping("/api/online") public class SysOnlineController { @Resource private ISysOnlineService onlineService; @ApiOperation("在线用户列表") @GetMapping("/getOnlineUserList") public CommonResult> getOnlineUserList(@RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo list = onlineService.getOnlineUserList(page, pageSize); return CommonResult.success(list); } @ApiOperation("强制用户退出") @PostMapping("/removeUser/{token}") @Log(title = "在线用户模块", businessType = BusinessType.CLEAN) @PreAuthorize("@ss.hasPermission('monitor:online:forceLogout')") public CommonResult removeUser(@PathVariable("token") String sessionId) { onlineService.removeUser(sessionId); return CommonResult.success(); } @ApiOperation("批量强制用户退出") @PostMapping("/removeUser") @Log(title = "在线用户模块", businessType = BusinessType.CLEAN) @PreAuthorize("@ss.hasPermission('monitor:online:batchLogout')") public CommonResult removeUser(@RequestBody Set ids) { onlineService.removeUser(ids); return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysPostController.java ================================================ package ginyi.server.admin.controller; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import ginyi.common.annotation.Log; import ginyi.common.enums.BusinessType; import ginyi.common.result.CommonResult; import ginyi.common.swagger.AddGroup; import ginyi.system.domain.model.dto.DeptDto; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.PostVo; import ginyi.system.service.ISysPostService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Set; @Api(tags = "岗位模块") @RestController @RequestMapping("/api/post") public class SysPostController { @Resource private ISysPostService postService; @ApiOperation("岗位详情") @GetMapping("/getPostById/{postId}") @PreAuthorize("@ss.hasPermission('system:post:edit')") public CommonResult getPostByDeptId(@PathVariable("postId") Long postId) { PostVo postVo = postService.getPostByPostId(postId); return CommonResult.success(postVo); } @ApiOperation("删除岗位") @PostMapping("/delete/{postId}") @PreAuthorize("@ss.hasPermission('system:post:remove')") @Log(title = "岗位模块", businessType = BusinessType.DELETE) public CommonResult delete(@PathVariable("postId") Long postId) { postService.removePostById(postId); return CommonResult.success(); } @ApiOperation("批量删除岗位") @PostMapping("/delete") @PreAuthorize("@ss.hasPermission('system:post:remove')") @Log(title = "岗位模块", businessType = BusinessType.DELETE) public CommonResult delete(@RequestBody Set ids) { postService.removeDeptByIds(ids); return CommonResult.success(); } @ApiOperation("岗位列表") @PostMapping("/list") @PreAuthorize("@ss.hasPermission('system:post:list')") @ApiOperationSupport(ignoreParameters = { "postDto.createTime", "postDto.params", "postDto.updateBy", "postDto.updateTime", "postDto.postId", "postDto.sort", }) public CommonResult> list(@RequestBody PostDto postDto, @RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo list = postService.list(postDto, page, pageSize); return CommonResult.success(list); } @ApiOperation("新增岗位") @PostMapping("/add") @PreAuthorize("@ss.hasPermission('system:post:add')") @Log(title = "岗位模块", businessType = BusinessType.INSERT) @ApiOperationSupport(ignoreParameters = { "postDto.createBy", "postDto.createTime", "postDto.beginTime", "postDto.endTime", "postDto.params", "postDto.updateBy", "postDto.updateTime", "postDto.postId", }) public CommonResult addPost(@RequestBody @Validated(AddGroup.class) PostDto postDto) { postService.addPost(postDto); return CommonResult.success(); } @ApiOperation("更新岗位") @PostMapping("/update") @PreAuthorize("@ss.hasPermission('system:post:edit')") @Log(title = "岗位模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(ignoreParameters = { "postDto.createBy", "postDto.createTime", "postDto.beginTime", "postDto.endTime", "postDto.params", "postDto.updateBy", "postDto.updateTime", }) public CommonResult update(@RequestBody @Validated PostDto postDto){ postService.updatePost(postDto); return CommonResult.success(); } @ApiOperation("更新岗位状态") @PostMapping("/updateStatus") @PreAuthorize("@ss.hasPermission('system:post:edit')") @Log(title = "岗位模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(includeParameters = { "postDto.postId", "postDto.status" }) public CommonResult updateStatus(@RequestBody PostDto postDto){ postService.updateStatus(postDto); return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysRoleController.java ================================================ package ginyi.server.admin.controller; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import ginyi.common.annotation.Log; import ginyi.common.enums.BusinessType; import ginyi.common.result.CommonResult; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.RoleVo; import ginyi.system.service.ISysRoleService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Set; @Api(tags = "角色模块") @RestController @RequestMapping("/api/role") public class SysRoleController { @Resource private ISysRoleService roleService; @ApiOperation("角色列表") @PostMapping("/list") @PreAuthorize("@ss.hasPermission('system:role:query')") @ApiOperationSupport(ignoreParameters = { "roleDto.updateBy", "roleDto.updateTime", "roleDto.createTime", "roleDto.params", "roleDto.sort", "roleDto.permissions", "roleDto.roleId", }) public CommonResult> list(@RequestBody @Validated RoleDto roleDto, @RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo list = roleService.list(roleDto, page, pageSize); return CommonResult.success(list); } @ApiOperation("角色详情") @GetMapping("/getRoleByRoleId/{roleId}") @PreAuthorize("@ss.hasPermission('system:role:edit')") public CommonResult getRoleByRoleId(@PathVariable("roleId") Long roleId){ RoleVo role = roleService.getRoleByRoleId(roleId); return CommonResult.success(role); } @ApiOperation("新增角色") @PostMapping("/add") @PreAuthorize("@ss.hasPermission('system:role:add')") @Log(title = "角色模块", businessType = BusinessType.INSERT) @ApiOperationSupport(ignoreParameters = { "roleDto.updateBy", "roleDto.updateTime", "roleDto.createBy", "roleDto.createTime", "roleDto.beginTime", "roleDto.endTime", "roleDto.params", "roleDto.roleId", }) public CommonResult addRole(@RequestBody @Validated(AddGroup.class) RoleDto roleDto){ roleService.addRole(roleDto); return CommonResult.success(); } @ApiOperation("更新角色") @PostMapping("/update") @PreAuthorize("@ss.hasPermission('system:role:add')") @Log(title = "角色模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(ignoreParameters = { "roleDto.updateBy", "roleDto.updateTime", "roleDto.createBy", "roleDto.createTime", "roleDto.beginTime", "roleDto.endTime", "roleDto.params", }) public CommonResult update(@RequestBody @Validated(UpdateGroup.class) RoleDto roleDto){ roleService.updateRole(roleDto); return CommonResult.success(); } @ApiOperation("删除角色") @PostMapping("/delete/{roleId}") @PreAuthorize("@ss.hasPermission('system:role:remove')") @Log(title = "角色模块", businessType = BusinessType.DELETE) public CommonResult delete(@PathVariable("roleId") Long roleId){ roleService.removeByRoleId(roleId); return CommonResult.success(); } @ApiOperation("批量删除角色") @PreAuthorize("@ss.hasPermission('system:role:remove')") @PostMapping("/delete") @Log(title = "角色模块", businessType = BusinessType.DELETE) public CommonResult delete(@RequestBody Set ids){ roleService.removeByRoleIds(ids); return CommonResult.success(); } @ApiOperation("更新角色状态") @PostMapping("/updateStatus") @PreAuthorize("@ss.hasPermission('system:post:edit')") @Log(title = "角色模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(includeParameters = { "roleDto.roleId", "roleDto.status" }) public CommonResult updateStatus(@RequestBody RoleDto roleDto){ roleService.updateStatus(roleDto); return CommonResult.success(); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysUserController.java ================================================ package ginyi.server.admin.controller; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import ginyi.common.annotation.Log; import ginyi.common.enums.BusinessType; import ginyi.common.result.CommonResult; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.DeptDto; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.UserVo; import ginyi.system.service.ISysUserService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.HashMap; import java.util.Set; @Api(tags = "用户模块") @RestController @Slf4j @RequestMapping("/api/user") public class SysUserController { @Resource private ISysUserService userService; @ApiOperation("删除用户") @PostMapping("/delete/{userId}") @PreAuthorize("@ss.hasPermission('system:user:remove')") @Log(title = "用户模块", businessType = BusinessType.DELETE) public CommonResult delete(@PathVariable("userId") Long userId){ userService.removeById(userId); return CommonResult.success(); } @ApiOperation("用户详情") @GetMapping("/getUserByUserId/{userId}") @PreAuthorize("@ss.hasPermission('system:user:edit')") public CommonResult getUserByUserId(@PathVariable("userId") String userId) { UserVo user = userService.getUserByUserId(userId); return CommonResult.success(user); } @ApiOperation("批量删除用户") @PostMapping("/delete") @PreAuthorize("@ss.hasPermission('system:user:remove')") @Log(title = "用户模块", businessType = BusinessType.DELETE) public CommonResult delete(@RequestBody Set ids){ userService.removeUserByIds(ids); return CommonResult.success(); } @ApiOperation("更新用户状态") @PostMapping("/updateStatus") @PreAuthorize("@ss.hasPermission('system:user:edit')") @Log(title = "用户模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(includeParameters = { "userDto.userId", "userDto.status" }) public CommonResult updateStatus(@RequestBody UserDto userDto){ userService.updateStatus(userDto); return CommonResult.success(); } @ApiOperation("添加用户") @PostMapping("/add") @PreAuthorize("@ss.hasPermission('system:user:add')") @Log(title = "用户模块", businessType = BusinessType.INSERT) @ApiOperationSupport(ignoreParameters = { "userDto.beginTime", "userDto.endTime", "userDto.createBy", "userDto.createTime", "userDto.deleted", "userDto.loginDate", "userDto.loginIp", "userDto.params", "userDto.updateBy", "userDto.updateTime", "userDto.userId", "userDto.password", }) public CommonResult add(@RequestBody @Validated(AddGroup.class) UserDto userDto) { userService.addUser(userDto); return CommonResult.success(); } @ApiOperation("更新用户") @PostMapping("/update") @PreAuthorize("@ss.hasPermission('system:user:edit')") @Log(title = "用户模块", businessType = BusinessType.UPDATE) @ApiOperationSupport(ignoreParameters = { "userDto.beginTime", "userDto.endTime", "userDto.createBy", "userDto.createTime", "userDto.deleted", "userDto.loginDate", "userDto.loginIp", "userDto.params", "userDto.updateBy", "userDto.updateTime", "userDto.password", }) public CommonResult update(@RequestBody @Validated(UpdateGroup.class) UserDto userDto) { userService.updateUser(userDto); return CommonResult.success(); } @ApiOperation("用户列表") @PostMapping("/list") @PreAuthorize("@ss.hasPermission('system:user:list')") @ApiOperationSupport(ignoreParameters = { "userDto.deleted", "userDto.loginDate", "userDto.loginIp", "userDto.params", "userDto.userId", "userDto.password", "userDto.avatar", "userDto.updateBy", "userDto.updateTime", "userDto.createTime", "userDto.deptId", "userDto.postIds", "userDto.roleIds", }) public CommonResult> list(@RequestBody UserDto userDto, @RequestParam(value = "page", required = false) Long page, @RequestParam(value = "pageSize", required = false) Long pageSize) { BaseVo baseVo = userService.list(userDto, page, pageSize); return CommonResult.success(baseVo); } @ApiOperation("根据部门id获取用户") @PostMapping("getUserListByDeptIds") @ApiOperationSupport(ignoreParameters = { "deptDto.deptId", "deptDto.parentId", "deptDto.ancestors", "deptDto.deptName", "deptDto.sort", "deptDto.leader", "deptDto.phone", "deptDto.email", "deptDto.beginTime", "deptDto.endTime", "deptDto.updateBy", "deptDto.updateTime", "deptDto.createBy", "deptDto.createTime", "deptDto.remark", "deptDto.params", "deptDto.status", }) public CommonResult>> getUserListByDeptIds(@RequestBody DeptDto deptDto){ BaseVo> userList = userService.getUserListByDeptIds(deptDto); return CommonResult.success(userList); } @ApiOperation("根据岗位id获取用户") @PostMapping("getUserListByPostIds") @ApiOperationSupport(ignoreParameters = { "postDto.postId", "postDto.postName", "postDto.sort", "postDto.postCode", "postDto.beginTime", "postDto.endTime", "postDto.updateBy", "postDto.updateTime", "postDto.createBy", "postDto.createTime", "postDto.remark", "postDto.params", "postDto.status", }) public CommonResult>> getUserListByPostIds(@RequestBody PostDto postDto){ BaseVo> userList = userService.getUserListByPostIds(postDto); return CommonResult.success(userList); } @ApiOperation("根据角色id获取用户") @PostMapping("getUserListByRoleIds") @ApiOperationSupport(ignoreParameters = { "roleDto.roleId", "roleDto.roleName", "roleDto.sort", "roleDto.roleKey", "roleDto.beginTime", "roleDto.endTime", "roleDto.updateBy", "roleDto.updateTime", "roleDto.createBy", "roleDto.createTime", "roleDto.remark", "roleDto.params", "roleDto.status", "roleDto.permissions", }) public CommonResult>> getUserListByPostIds(@RequestBody RoleDto roleDto){ BaseVo> userList = userService.getUserListByRoleIds(roleDto); return CommonResult.success(userList); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/controller/SysVerifyController.java ================================================ package ginyi.server.admin.controller; import ginyi.common.result.CommonResult; import ginyi.system.service.IVerifyService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; @RestController @Api(tags = "验证码模块") @RequestMapping("/api/verify") public class SysVerifyController { @Resource private IVerifyService verifyService; @ApiOperation("图片验证码") @GetMapping("/captcha") public CommonResult captcha(){ String img = verifyService.captcha(); HashMap map = new HashMap<>(); map.put("img", img); return CommonResult.success(map); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/java/ginyi/server/admin/swagger/AdminSwagger2Config.java ================================================ package ginyi.server.admin.swagger; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import ginyi.common.swagger.Swagger2Config; import org.springframework.context.annotation.Configuration; import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSwagger2 @EnableKnife4j @Configuration public class AdminSwagger2Config extends Swagger2Config { } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/resources/config/application.yml ================================================ # 说明: # 1、这是 ginyi-server-admin 模块下特有的配置项 # 2、更多全局通用的配置项在 ginyi-system 系统应用服务下 # 3、如果需要对重写全局的配置项的话,可以将其对应的属性配置拷贝至本配置中,会自动覆盖! # 应用服务 server: # 服务端口 port: 8066 # 接口文档的配置 swagger: group-name: admin # 接口文档url swagger-url: 写点什么呢~~~ # 接口文档标题 swagger-title: Ginyi-admin api doc # 接口文档版本号 swaggerversion: 1.0 # 接口文档描述 swagger-description: 这是swagger接口文档~ ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-admin/src/main/resources/static/i18n/messages.properties ================================================ #错误消息 not.null=* 必须填写 user.jcaptcha.error=验证码错误 user.jcaptcha.expire=验证码已失效 user.not.exists=用户不存在/密码错误 user.password.not.match=用户不存在/密码错误 user.password.retry.limit.count=密码输入错误{0}次 user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 user.password.delete=对不起,您的账号已被删除 user.blocked=用户已封禁,请联系管理员 role.blocked=角色已封禁,请联系管理员 user.logout.success=退出成功 length.not.valid=长度必须在{min}到{max}个字符之间 user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 user.password.not.valid=* 5-50个字符 user.email.not.valid=邮箱格式错误 user.mobile.phone.number.not.valid=手机号格式错误 user.login.success=登录成功 user.register.success=注册成功 user.notfound=请重新登录 user.forcelogout=管理员强制退出,请重新登录 user.unknown.error=未知错误,请重新登录 ##文件上传消息 upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{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}] ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-common/pom.xml ================================================ ginyi-server com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-server-common ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-web/pom.xml ================================================ ginyi-server com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-server-web io.springfox springfox-swagger2 io.springfox springfox-swagger-ui com.github.xiaoymin knife4j-spring-boot-starter src/main/resources **/*.* false src/main/java **/*.xml false org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-web/src/main/java/ginyi/server/web/WebApplication.java ================================================ package ginyi.server.web; import ginyi.framework.core.config.AppConfig; import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.data.redis.core.RedisTemplate; import javax.sql.DataSource; import java.net.UnknownHostException; @Slf4j // 扫描系统服务的包以及自身所在模块的包 @SpringBootApplication(scanBasePackages = { "ginyi.system", "ginyi.common", "ginyi.framework.websocket", "ginyi.framework.security", "ginyi.server.web" }) @MapperScan({ "ginyi.system.**.mapper", "ginyi.server.admin.**.mapper" }) public class WebApplication { public static void main(String[] args) throws UnknownHostException { ConfigurableApplicationContext context = SpringApplication.run(WebApplication.class, args); AppConfig.printAppInfo(context); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-web/src/main/java/ginyi/server/web/controller/Test.java ================================================ package ginyi.server.web.controller; import ginyi.common.result.CommonResult; import ginyi.system.domain.model.dto.LoginDto; import ginyi.system.domain.model.vo.LoginVo; import ginyi.system.service.ISysLoginService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/user") @Api(tags = "登录模块") @Slf4j public class Test { @Autowired private ISysLoginService loginService; @ApiOperation("测试用户登录") @PostMapping("/login") public CommonResult login(@RequestBody @Validated LoginDto loginDto) { LoginVo loginVo = loginService.login(loginDto); return CommonResult.success(loginVo); } } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-web/src/main/java/ginyi/server/web/swagger/WebSwagger2Config.java ================================================ package ginyi.server.web.swagger; import ginyi.common.swagger.Swagger2Config; import org.springframework.context.annotation.Configuration; import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSwagger2 @Configuration public class WebSwagger2Config extends Swagger2Config { } ================================================ FILE: ginyi-springboot/ginyi-server/ginyi-server-web/src/main/resources/config/application.yml ================================================ # 说明: # 1、这是 ginyi-server-web 模块下特有的配置项 # 2、更多全局通用的配置项在 ginyi-system 系统应用服务下 # 3、如果需要对重写全局的配置项的话,可以将其对应的属性配置拷贝至本配置中,会自动覆盖! # 应用服务 server: # 服务端口 port: 8055 # 接口文档的配置 swagger: group-name: web # 接口文档url swagger-url: 写点什么呢~~~ # 接口文档标题 swagger-title: Ginyi-web api doc # 接口文档版本号 swaggerversion: 1.0 # 接口文档描述 swagger-description: 这是swagger接口文档~ ================================================ FILE: ginyi-springboot/ginyi-server/pom.xml ================================================ ginyi-springboot com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-server pom ginyi-server-web ginyi-server-admin ginyi-server-common com.ginyi ginyi-framework-security ${ginyi.version} com.ginyi ginyi-framework-core ${ginyi.version} com.ginyi ginyi-framework-websocket ${ginyi.version} ================================================ FILE: ginyi-springboot/ginyi-system/pom.xml ================================================ ginyi-springboot com.ginyi 0.0.1-SNAPSHOT 4.0.0 ginyi-system org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test com.ginyi ginyi-common-redis ${ginyi.version} com.ginyi ginyi-common-mysql ${ginyi.version} com.ginyi ginyi-common-swagger ${ginyi.version} com.ginyi ginyi-common-exception ${ginyi.version} com.ginyi ginyi-common-result ${ginyi.version} com.ginyi ginyi-common-utils ${ginyi.version} com.ginyi ginyi-common-constants ${ginyi.version} org.springframework.boot spring-boot-starter-validation org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-data-mongodb ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/BaseEntity.java ================================================ package ginyi.system.domain; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import ginyi.common.annotation.CreateBy; import ginyi.common.annotation.CreateTime; import ginyi.common.annotation.UpdateBy; import ginyi.common.annotation.UpdateTime; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 基础 entity */ @Data public class BaseEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 搜索值 */ @JsonIgnore @TableField(exist = false, select = false) private String searchValue; /** * 创建者 */ @ApiModelProperty("创建者") @CreateBy @TableField(fill = FieldFill.INSERT) private String createBy; /** * 创建时间 */ @JsonFormat @ApiModelProperty("创建时间") @CreateTime @TableField(fill = FieldFill.INSERT) private Date createTime; /** * 更新者 */ @ApiModelProperty("更新者") @UpdateBy @TableField(fill = FieldFill.INSERT_UPDATE) private String updateBy; /** * 更新时间 */ @JsonFormat @ApiModelProperty("更新时间") @UpdateTime @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; /** * 备注 */ @ApiModelProperty("备注") private String remark; /** * 请求参数 */ @JsonInclude(JsonInclude.Include.NON_EMPTY) @TableField(exist = false, select = false) private Map params; public Map getParams() { if (params == null) { params = new HashMap<>(); } return params; } public void setParams(Map params) { this.params = params; } } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/LoginUser.java ================================================ package ginyi.system.domain; import com.alibaba.fastjson2.annotation.JSONField; import ginyi.system.domain.SysUser; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Set; /** * 登录用户身份权限 * * @author ruoyi */ @Data public class LoginUser implements UserDetails { private static final long serialVersionUID = 1L; /** * 用户ID */ private Long userId; /** * 部门ID */ private Long deptId; /** * 用户唯一标识 */ private String token; /** * 登录时间 */ private Long loginTime; /** * 过期时间 */ private Long expireTime; /** * 登录IP地址 */ private String ipaddr; /** * 登录地点 */ private String loginLocation; /** * 浏览器类型 */ private String browser; /** * 操作系统 */ private String os; /** * 权限列表 */ private Set permissions; /** * 用户信息 */ private SysUser user; public LoginUser() { } public LoginUser(SysUser user, Set permissions) { this.user = user; this.permissions = permissions; } public LoginUser(Long userId, Long deptId, SysUser user, Set permissions) { this.userId = userId; this.deptId = deptId; this.user = user; this.permissions = permissions; } @JSONField(serialize = false) @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getUserName(); } /** * 账户是否未过期,过期无法验证 */ @JSONField(serialize = false) @Override public boolean isAccountNonExpired() { return true; } /** * 指定用户是否解锁,锁定的用户无法进行身份验证 * * @return */ @JSONField(serialize = false) @Override public boolean isAccountNonLocked() { return true; } /** * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 * * @return */ @JSONField(serialize = false) @Override public boolean isCredentialsNonExpired() { return true; } /** * 是否可用 ,禁用的用户不能身份验证 * * @return */ @JSONField(serialize = false) @Override public boolean isEnabled() { return true; } @Override public Collection getAuthorities() { return null; } } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/Sys.java ================================================ package ginyi.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * 系统相关信息 * * @author ruoyi */ @Data public class Sys { /** * 服务器名称 */ @ApiModelProperty("服务器名称") private String computerName; /** * 服务器Ip */ @ApiModelProperty("服务器ip") private String computerIp; /** * 项目路径 */ @ApiModelProperty("项目路径") private String userDir; /** * 操作系统 */ @ApiModelProperty("操作系统") private String osName; /** * 系统架构 */ @ApiModelProperty("系统架构") private String osArch; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysConfig.java ================================================ package ginyi.system.domain; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; /** * 参数配置表 sys_config * * @author ruoyi */ @Data public class SysConfig extends BaseEntity { private static final long serialVersionUID = 1L; /** * 参数主键 */ @TableId private Long configId; /** * 参数名称 */ private String configName; /** * 参数键名 */ private String configKey; /** * 参数键值 */ private String configValue; /** * 系统内置(Y是 N否) */ private String configType; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysCpu.java ================================================ package ginyi.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * CPU相关信息 * @author ruoyi */ @Data public class SysCpu { /** * 核心数,直接回显数字 */ @ApiModelProperty("核心数") private int cpuNum; /** * CPU总的使用率,不需要回显 */ @ApiModelProperty("CPU总使用率,不需要回显") private double total; /** * CPU系统使用率,拼接 % 回显 */ @ApiModelProperty("CPU系统使用率") private double sys; /** * CPU用户使用率,拼接 % 回显 */ @ApiModelProperty("CPU用户使用率") private double used; /** * CPU当前等待率,拼接 % 回显 */ @ApiModelProperty("CPU当前等待率") private double wait; /** * CPU当前空闲率,拼接 % 回显 */ @ApiModelProperty("CPU当前空闲率") private double free; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysDept.java ================================================ package ginyi.system.domain; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.ArrayList; import java.util.List; /** * 部门表 sys_dept * * @author ruoyi */ @EqualsAndHashCode(callSuper = true) @Data public class SysDept extends BaseEntity { private static final long serialVersionUID = 1L; /** * 部门ID */ @ApiModelProperty("部门id") @TableId private Long deptId; /** * 父部门ID */ @ApiModelProperty("父级id") private Long parentId; /** * 祖级列表 */ private String ancestors; /** * 部门名称 */ @ApiModelProperty("部门名称") @NotBlank(message = "部门名称不能为空") @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") private String deptName; /** * 显示顺序 */ @ApiModelProperty("显示顺序") @NotNull(message = "显示顺序不能为空") private Integer sort; /** * 负责人 */ @ApiModelProperty("部门负责人") private String leader; /** * 联系电话 */ @ApiModelProperty("部门负责人电话") @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") private String phone; /** * 邮箱 */ @ApiModelProperty("部门负责人邮箱") @Email(message = "邮箱格式不正确") @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") private String email; /** * 部门状态:0正常,1停用 */ @ApiModelProperty("状态,0正常,1停用") private String status; /** * 删除标志 */ @TableLogic private String deleted; /** * 父部门名称 */ @TableField(select = false, exist = false) @ApiModelProperty("父级部门名称") private String parentName; /** * 子部门 */ @TableField(select = false, exist = false) @ApiModelProperty("子部门") private List children = new ArrayList(); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysFile.java ================================================ package ginyi.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * 系统文件相关信息 * * @author ruoyi */ @Data public class SysFile { /** * 盘符路径 */ @ApiModelProperty("盘符路径") private String dirName; /** * 文件类型 */ @ApiModelProperty("文件类型") private String typeName; /** * 总大小,单位G */ @ApiModelProperty("总大小,单位G") private double total; /** * 剩余大小,单位G */ @ApiModelProperty("可用大小,单位G") private double free; /** * 已经使用量,单位G */ @ApiModelProperty("已用大小,单位G") private double used; /** * 资源的使用率 */ @ApiModelProperty("已用百分比") private double usage; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysJvm.java ================================================ package ginyi.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * JVM相关信息 * * @author ruoyi */ @Data public class SysJvm { /** * 当前JVM占用的内存总数,单位M */ @ApiModelProperty("当前JVM占用的内存总数,单位M") private double total; /** * JVM最大可用内存总数,单位M */ @ApiModelProperty("JVM最大可用内存总数,单位M") private double max; /** * JVM空闲内存,单位M */ @ApiModelProperty("JVM空闲内存,单位M") private double free; /** * JDK名称 */ @ApiModelProperty("JDK名称") private String name; /** * JDK版本 */ @ApiModelProperty("JDK版本") private String version; /** * JDK路径 */ @ApiModelProperty("JDK路径") private String home; /** * 已用内存,单位M */ @ApiModelProperty("已用内存,单位M") private double used; /** * 使用率 */ @ApiModelProperty("内存使用率") private double usage; /** * 运行参数 */ @ApiModelProperty("运行参数") private String inputArgs; /** * 运行时间 */ @ApiModelProperty("运行时间") private String runTime; /** * 启动时间 */ @ApiModelProperty("启动时间") private String startTime; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysLogLogin.java ================================================ package ginyi.system.domain; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; @Data @ApiModel("登录日志返回数据") @Document("sys_log_login") public class SysLogLogin extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("ID") private String id; @ApiModelProperty("用户账号") private String userName; @ApiModelProperty("登录状态 0成功 1失败") private String status; @ApiModelProperty("登录IP地址") private String ipaddr; @ApiModelProperty("登录地点") private String loginLocation; @ApiModelProperty("浏览器类型") private String browser; @ApiModelProperty("操作系统") private String os; @ApiModelProperty("提示消息") private String msg; @JsonFormat @ApiModelProperty("登录时间") private Date loginTime; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysLogOperation.java ================================================ package ginyi.system.domain; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("操作日志返回数据") @Document("sys_log_operation") public class SysLogOperation extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("ID") private String id; @ApiModelProperty("操作模块") private String title; @ApiModelProperty("业务类型(0其它 1新增 2修改 3删除)") private Integer businessType; @ApiModelProperty("业务类型数组") private Integer[] businessTypes; @ApiModelProperty("请求方法") private String method; @ApiModelProperty("请求方式") private String requestMethod; @ApiModelProperty("操作类别(0其它 1后台用户 2手机端用户)") private Integer operatorType; @ApiModelProperty("操作人员") private String operationName; @ApiModelProperty("部门名称") private String deptName; @ApiModelProperty("请求URL") private String operationUrl; @ApiModelProperty("操作地址") private String operationIp; @ApiModelProperty("操作地点") private String operationLocation; @ApiModelProperty("请求参数") private String operationParam; @ApiModelProperty("返回参数") private String jsonResult; @ApiModelProperty("操作状态(0正常 1异常)") private Integer status; @ApiModelProperty("错误消息") private String errorMsg; @JsonFormat @ApiModelProperty("操作时间") private Date operationTime; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysMemory.java ================================================ package ginyi.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * 內存相关信息 * * @author ruoyi */ @Data public class SysMemory { /** * 内存总量,单位G */ @ApiModelProperty("内存总数,单位G") private double total; /** * 已用内存,单位G */ @ApiModelProperty("已用内存,单位G") private double used; /** * 剩余内存,单位G */ @ApiModelProperty("可用内存,单位G") private double free; /** * 内存使用率 */ @ApiModelProperty("使用率") private double usage; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysMenu.java ================================================ package ginyi.system.domain; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.ArrayList; import java.util.List; /** * 菜单权限表 sys_menu * * @author ruoyi */ @Data public class SysMenu extends BaseEntity { private static final long serialVersionUID = 1L; /** * 菜单ID */ @TableId @ApiModelProperty("菜单ID") private Long menuId; /** * 菜单名称 */ @ApiModelProperty("菜单名称") private String menuName; /** * 父菜单名称 */ @ApiModelProperty("父级菜单名称") @TableField(exist = false, select = false) private String parentName; /** * 父菜单ID */ @ApiModelProperty("父级菜单ID") private Long parentId; /** * 路由name,用于路由跳转 */ @ApiModelProperty("路由名称") private String name; /** * 显示顺序 */ @ApiModelProperty("显示顺序") private Integer sort; /** * 路由地址 */ @ApiModelProperty("路由地址") @TableField(value = "path") private String path; /** * 组件路径 */ @ApiModelProperty("组件路径") private String component; /** * 路由参数 */ @ApiModelProperty("路由参数") private String query; /** * 是否为外链(0是 1否) */ @ApiModelProperty("是否为外链(0是 1否)") private String isFrame; /** * 是否缓存(0缓存 1不缓存) */ @ApiModelProperty("是否缓存(0缓存 1不缓存)") private String isCache; /** * 类型(M目录 C菜单 F按钮) */ @ApiModelProperty("类型(M目录 C菜单 F按钮)") private String menuType; /** * 显示状态(0显示 1隐藏) */ @ApiModelProperty("显示状态(0显示 1隐藏)") private String visible; /** * 菜单状态(0正常 1停用) */ @ApiModelProperty("菜单状态(0正常 1停用)") private String status; /** * 权限字符串 */ @ApiModelProperty("权限字符串") private String perms; /** * 菜单图标 */ @ApiModelProperty("菜单图标") private String icon; /** * 子菜单 */ @ApiModelProperty("子菜单") @TableField(exist = false, select = false) private List children = new ArrayList(); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysNotice.java ================================================ package ginyi.system.domain; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; @EqualsAndHashCode(callSuper = true) @Data public class SysNotice extends BaseEntity { /** * 通知的id */ @ApiModelProperty("通知的id") private Long noticeId; /** * 通知标题 */ @ApiModelProperty("通知标题") private String title; /** * 通知的类型(0通知,1公告) */ @ApiModelProperty("通知的类型(0通知,1公告)") private String type; /** * 通知的内容 */ @ApiModelProperty("通知的内容") private String content; /** * 通知的用户 */ @ApiModelProperty("通知的用户") private List userIds; /** * 已读的用户 */ @ApiModelProperty("已读的用户") private List userReadIds; /** * 通知的状态(0正常,1关闭) */ @ApiModelProperty("通知的状态(0正常,1关闭)") private String status; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysPost.java ================================================ package ginyi.system.domain; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.ibatis.annotations.Select; /** * 岗位表 sys_post * * @author ruoyi */ @EqualsAndHashCode(callSuper = true) @Data public class SysPost extends BaseEntity { private static final long serialVersionUID = 1L; /** * 岗位序号 */ @TableId private Long postId; /** * 岗位编码 */ private String postCode; /** * 岗位名称 */ private String postName; /** * 岗位排序 */ private Integer sort; /** * 状态(0正常 1停用) */ private String status; /** * 用户是否存在此岗位标识 默认不存在 */ @TableField(select = false, exist = false) private boolean flag = false; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysRole.java ================================================ package ginyi.system.domain; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Set; /** * 角色表 sys_role * * @author ruoyi */ @Data public class SysRole extends BaseEntity { private static final long serialVersionUID = 1L; /** * 角色ID */ @TableId private Long roleId; /** * 角色名称 */ @NotBlank(message = "角色名称不能为空") @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") private String roleName; /** * 角色权限 */ @NotBlank(message = "权限字符不能为空") @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") private String roleKey; /** * 角色排序 */ @NotNull(message = "显示顺序不能为空") private Integer sort; /** * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ @TableField(select = false, exist = false) private String dataScope; /** * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ @TableField(select = false, exist = false) private boolean menuCheckStrictly; /** * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ @TableField(select = false, exist = false) private boolean deptCheckStrictly; /** * 角色状态(0正常 1停用) */ private String status; /** * 删除标志(0代表存在 2代表删除) */ @TableLogic private String deleted; /** * 用户是否存在此角色标识 默认不存在 */ @TableField(select = false, exist = false) private boolean flag = false; /** * 菜单组 */ @TableField(select = false, exist = false) private Long[] menuIds; /** * 部门组(数据权限) */ @TableField(select = false, exist = false) private Long[] deptIds; /** * 角色菜单权限 */ @TableField(select = false, exist = false) private Set permissions; public boolean isAdmin() { return isAdmin(this.roleId); } public static boolean isAdmin(Long roleId) { return roleId != null && 1L == roleId; } } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysServer.java ================================================ package ginyi.system.domain; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.List; /** * 服务器相关信息 * * @author ruoyi */ @Data public class SysServer { /** * 服务器信息 */ @ApiModelProperty("服务器信息") private Sys sys; /** * cpu */ @ApiModelProperty("cpu相关信息") private SysCpu cpu; /** * 硬盘 */ @ApiModelProperty("系统硬盘") private List file; /** * jvm */ @ApiModelProperty("Java虚拟机") private SysJvm jvm; /** * 内存 */ @ApiModelProperty("内存信息") private SysMemory memory; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/SysUser.java ================================================ package ginyi.system.domain; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import java.util.Date; import java.util.List; @Data public class SysUser extends BaseEntity { private static final long serialVersionUID = 1L; /** * 用户ID */ @TableId private Long userId; /** * 部门ID */ private Long deptId; /** * 用户账号 */ private String userName; /** * 用户昵称 */ private String nickName; /** * 用户邮箱 */ private String email; /** * 手机号码 */ private String phoneNumber; /** * 用户性别 */ private String sex; /** * 用户头像 */ private String avatar; /** * 密码 */ private String password; /** * 帐号状态(0正常 1停用) */ private String status; /** * 删除标志(0代表存在 1代表删除) */ @TableLogic private String deleted; /** * 最后登录IP */ private String loginIp; /** * 最后登录时间 */ private Date loginDate; /** * 备注 */ private String remark; /** * 部门对象 */ @TableField(exist = false, select = false) private SysDept dept; /** * 角色对象 */ @TableField(exist = false, select = false) private List roles; /** * 角色组 */ @TableField(exist = false, select = false) private Long[] roleIds; /** * 岗位对象 */ @TableField(exist = false, select = false) private List posts; /** * 岗位组 */ @TableField(exist = false, select = false) private Long[] postIds; /** * 角色ID */ @TableField(exist = false, select = false) private Long roleId; public boolean isAdmin() { return isAdmin(this.userId); } public static boolean isAdmin(Long userId) { return userId != null && 1L == userId; } } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/CacheDto.java ================================================ package ginyi.system.domain.model.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @ApiModel("缓存数据请求参数") @Data public class CacheDto { /** * 键名 */ @ApiModelProperty("键名") private String key; /** * 类型 */ @ApiModelProperty("数据类型") private String type; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/DeptDto.java ================================================ package ginyi.system.domain.model.dto; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.*; import java.util.Date; import java.util.List; @EqualsAndHashCode(callSuper = true) @Data public class DeptDto extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("部门id") @NotNull(groups = UpdateGroup.class, message = "部门id不能为空") private Long deptId; /** * 根据部门ids获取用户列表专用 */ @ApiModelProperty("部门id(根据部门ids获取用户列表专用)") private List deptIds; @ApiModelProperty("父级id") private Long parentId; private String ancestors; @ApiModelProperty("部门名称") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "部门名称不能为空") @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") private String deptName; @ApiModelProperty("显示顺序") @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "显示顺序不能为空") private Integer sort; @ApiModelProperty("部门负责人") private String leader; @ApiModelProperty("部门负责人电话") @Pattern(regexp = "(?:0|86|\\+86)?1[3-9]\\d{9}", message = "手机号码格式不正确") private String phone; @ApiModelProperty("部门负责人邮箱") @Email(message = "邮箱格式不正确") @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") private String email; @ApiModelProperty("状态,0正常,1停用") @Size(min = 0, max = 1, message = "状态不合法,0正常,1停用") private String status; @ApiModelProperty("创建时间,开始时间") private Date beginTime; @ApiModelProperty("创建时间,结束时间") private Date endTime; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/LoginDto.java ================================================ package ginyi.system.domain.model.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotEmpty; /** * 用户登录对象 * * @author ruoyi */ @Data @ApiModel("登录用户参数") public class LoginDto { @ApiModelProperty(value = "用户名", required = true) @NotEmpty(message = "用户名不能为空") @Length(min = 5, max = 15, message = "用户名长度必须在5~15个字符之间") private String username; @ApiModelProperty(value = "密码", required = true) @NotEmpty(message = "密码不能为空") @Length(min = 6, max = 15, message = "密码长度必须在6~15个字符之间") private String password; @ApiModelProperty(value = "验证码") private String code; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/MenuDto.java ================================================ package ginyi.system.domain.model.dto; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统菜单请求参数") public class MenuDto extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("菜单id") @NotNull(groups = UpdateGroup.class, message = "菜单id不能为空") private Long menuId; @ApiModelProperty("菜单名称") @Size(max = 50, message = "菜单名称长度不能超过50个字符") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "菜单名称不能为空") private String menuName; @ApiModelProperty("父级id") private Long parentId; @ApiModelProperty("排序(1升序,0降序)") @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "排序不能为空") private Integer sort = 1; @ApiModelProperty("路由名称") @Size(max = 50, message = "路由名称长度不得超过50个字符") private String name; @ApiModelProperty("路由地址") @Size(max = 200, message = "路由地址长度不得超过200个字符") private String path; @ApiModelProperty("组件路径") @Size(max = 200, message = "组件路径长度不得超过200个字符") private String component; @ApiModelProperty("类型(M目录 C菜单 F按钮)") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "菜单类型不能为空") private String menuType; @ApiModelProperty("显示状态(0显示 1隐藏)") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "显示状态不能为空") private String visible; @ApiModelProperty("菜单状态(0正常 1停用)") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "菜单状态不能为空") private String status; @ApiModelProperty("路由参数") private String query; @ApiModelProperty("是否为外链(0是 1否)") private String isFrame; @ApiModelProperty("是否缓存(0缓存 1不缓存)") private String isCache; @ApiModelProperty("权限字符串") private String perms; @ApiModelProperty("菜单图标") private String icon; @ApiModelProperty("过滤按钮(0过滤 1不过滤)") private String filterButton; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/NoticeDto.java ================================================ package ginyi.system.domain.model.dto; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.List; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统通知公告请求参数") public class NoticeDto extends BaseEntity { private static final long serialVersionUID = 1L; /** * 通知的id */ @ApiModelProperty("通知的id") @NotNull(groups = UpdateGroup.class, message = "通知id不能为空") private Long noticeId; /** * 通知标题 */ @ApiModelProperty("通知标题") @Size(max = 50, message = "通知标题长度不能超过50个字符") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "通知标题不能为空") private String title; /** * 通知的类型(0通知,1公告) */ @ApiModelProperty("通知的类型(0通知,1公告)") @NotEmpty(groups = {AddGroup.class, UpdateGroup.class}, message = "通知的类型不能为空") private String type; /** * 通知的内容 */ @ApiModelProperty("通知的内容") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "通知的内容不能为空") private String content; /** * 通知的用户 */ @ApiModelProperty("通知的用户") private List userIds; /** * 已读的用户 */ @ApiModelProperty("已读的用户") private List userReadIds; /** * 通知的状态(0正常,1关闭) */ @ApiModelProperty("通知的状态(0正常,1关闭)") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "通知的状态不能为空") private String status; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/PostDto.java ================================================ package ginyi.system.domain.model.dto; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Date; import java.util.List; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统岗位参数") public class PostDto extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("岗位id") @NotNull(groups = UpdateGroup.class, message = "岗位id不能为空") private Long postId; /** * 根据岗位ids获取用户列表专用 */ @ApiModelProperty("岗位id(根据岗位ids获取用户列表专用)") private List postIds; @ApiModelProperty("岗位编码") @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = "岗位编码不能为空") private String postCode; @ApiModelProperty("岗位名称") @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = "岗位名称不能为空") private String postName; @ApiModelProperty("岗位排序") @NotNull(groups = {UpdateGroup.class, AddGroup.class}, message = "岗位顺序不能为空") private Integer sort; @ApiModelProperty("状态(0正常 1停用)") @Size(min = 0, max = 1, message = "状态不合法") @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = "岗位状态不能为空") private String status; @ApiModelProperty("创建时间,开始时间") private Date beginTime; @ApiModelProperty("创建时间,结束时间") private Date endTime; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/RegisterDto.java ================================================ package ginyi.system.domain.model.dto; import io.swagger.annotations.ApiModel; import lombok.Data; @Data @ApiModel("用户注册参数") public class RegisterDto extends LoginDto { } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/RoleDto.java ================================================ package ginyi.system.domain.model.dto; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Date; import java.util.List; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统角色请求参数") public class RoleDto extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("角色ID") @NotNull(groups = UpdateGroup.class, message = "角色id不能为空") private Long roleId; /** * 根据角色ids获取用户列表专用 */ @ApiModelProperty("岗位id(根据角色ids获取用户列表专用)") private List roleIds; @ApiModelProperty("角色名称") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "角色名称不能为空") @Size(max = 20, message = "角色名称不能超过20个字符") private String roleName; @ApiModelProperty("角色权限字符串") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "角色权限字符串不能为空") @Size(max = 20, message = "角色权限字符不能超过20个字符") private String roleKey; @ApiModelProperty("角色排序") @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "角色排序不能为空") private Integer sort; @ApiModelProperty("角色状态(0正常 1停用)") @NotBlank(groups = {AddGroup.class, UpdateGroup.class}, message = "角色状态不能为空") @Size(max = 1, message = "状态不合法") private String status; @ApiModelProperty("角色菜单权限") private List permissions; @ApiModelProperty("创建时间,开始时间") private Date beginTime; @ApiModelProperty("创建时间,结束时间") private Date endTime; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/dto/UserDto.java ================================================ package ginyi.system.domain.model.dto; import com.baomidou.mybatisplus.annotation.TableLogic; import ginyi.common.swagger.AddGroup; import ginyi.common.swagger.UpdateGroup; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.*; import java.util.Date; import java.util.List; import java.util.Set; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统用户请求参数") public class UserDto extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("用户id") @NotNull(groups = UpdateGroup.class, message = "用户id不能为空") private Long userId; @ApiModelProperty("用户账号") @NotBlank(groups = {UpdateGroup.class, AddGroup.class}, message = "用户账号不能为空") private String userName; @ApiModelProperty("部门id") @NotNull(groups = {UpdateGroup.class, AddGroup.class}, message = "部门id不能为空") private Long deptId; @ApiModelProperty("用户昵称") private String nickName; @ApiModelProperty("邮箱") @Email(message = "邮箱格式不正确") private String email; @ApiModelProperty("手机号码") @Pattern(regexp = "(?:0|86|\\+86)?1[3-9]\\d{9}", message = "手机号码格式不正确") private String phoneNumber; @ApiModelProperty("性别(0男 1女 2未知)") @Size(min = 0, max = 2, message = "性别不合法,0男 1女 2未知") private String sex; @ApiModelProperty("头像") private String avatar; @ApiModelProperty("密码") private String password; @ApiModelProperty("状态(0正常 1停用)") @Size(min = 0, max = 1, message = "状态不合法") private String status; /** * 最后登录IP */ private String loginIp; /** * 最后登录时间 */ private Date loginDate; @ApiModelProperty("创建时间,开始时间") private Date beginTime; @ApiModelProperty("创建时间,结束时间") private Date endTime; @TableLogic private String deleted; @ApiModelProperty("岗位id") private List postIds; @ApiModelProperty("角色id") private List roleIds; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/BaseVo.java ================================================ package ginyi.system.domain.model.vo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.List; /** * 基础 vo */ @Data public class BaseVo { @ApiModelProperty("列表数据") private List list; @ApiModelProperty("总条数") private Integer count; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/CacheKeyVo.java ================================================ package ginyi.system.domain.model.vo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data public class CacheKeyVo { /** * 键名 */ @ApiModelProperty("键名") private String key; /** * 数据类型 */ @ApiModelProperty("类型") private String type; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/CacheVo.java ================================================ package ginyi.system.domain.model.vo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data public class CacheVo { /** * 键名 */ @ApiModelProperty("键名") private String key; /** * 键值 */ @ApiModelProperty("键名") private Object value; /** * 时间 */ @ApiModelProperty("剩余时间") private long expire; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/DeptVo.java ================================================ package ginyi.system.domain.model.vo; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统部门返回数据") public class DeptVo extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("部门id") private Long deptId; @ApiModelProperty("父级id") private Long parentId; private String ancestors; @ApiModelProperty("部门名称") private String deptName; @ApiModelProperty("显示顺序") private Integer sort; @ApiModelProperty("部门负责人") private String leader; @ApiModelProperty("部门负责人电话") private String phone; @ApiModelProperty("部门负责人邮箱") private String email; @ApiModelProperty("状态,0正常,1停用") private String status; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/LoginVo.java ================================================ package ginyi.system.domain.model.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.beans.factory.annotation.Value; @Data @ApiModel("登录返回数据") public class LoginVo { @ApiModelProperty(value = "Token令牌", required = true) private String token; @ApiModelProperty(value = "Token请求头Key", required = true) private String tokenHeader; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/NoticeVo.java ================================================ package ginyi.system.domain.model.vo; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统通知公告返回数据") public class NoticeVo extends BaseEntity { /** * 通知的id */ @ApiModelProperty("通知的id") private Long noticeId; /** * 通知标题 */ @ApiModelProperty("通知标题") private String title; /** * 通知的类型(0通知,1公告) */ @ApiModelProperty("通知的类型(0通知,1公告)") private String type; /** * 通知的内容 */ @ApiModelProperty("通知的内容") private String content; /** * 是否已读 */ @ApiModelProperty("是否已读") private boolean haveRead; /** * 通知的状态(0正常,1关闭) */ @ApiModelProperty("通知的状态(0正常,1关闭)") private String status; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/PostVo.java ================================================ package ginyi.system.domain.model.vo; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统岗位返回数据") public class PostVo extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("岗位序号") private Long postId; @ApiModelProperty("岗位编码") private String postCode; @ApiModelProperty("岗位名称") private String postName; @ApiModelProperty("岗位排序") private Integer sort; @ApiModelProperty("状态(0正常 1停用)") private String status; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/RoleVo.java ================================================ package ginyi.system.domain.model.vo; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; @EqualsAndHashCode(callSuper = true) @Data @ApiModel("系统角色返回数据") public class RoleVo extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("角色ID") private Long roleId; @ApiModelProperty("角色名称") private String roleName; @ApiModelProperty("角色权限字符串") private String roleKey; @ApiModelProperty("角色排序") private Integer sort; @ApiModelProperty("角色状态(0正常 1停用)") private String status; @ApiModelProperty("角色菜单权限") private List permissions; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/SessionUserVo.java ================================================ package ginyi.system.domain.model.vo; import lombok.Data; @Data public class SessionUserVo { /** * 用户唯一标识 */ private String token; /** * 登录时间 */ private Long loginTime; /** * 登录IP地址 */ private String ipaddr; /** * 登录地点 */ private String loginLocation; /** * 浏览器类型 */ private String browser; /** * 操作系统 */ private String os; /** * 登录名称 */ private String username; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/domain/model/vo/UserVo.java ================================================ package ginyi.system.domain.model.vo; import ginyi.system.domain.BaseEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.List; @Data @ApiModel("系统用户返回数据") public class UserVo extends BaseEntity { private static final long serialVersionUID = 1L; @ApiModelProperty("用户id") private Long userId; @ApiModelProperty("用户账号") private String userName; @ApiModelProperty("部门id") private Long deptId; @ApiModelProperty("用户昵称") private String nickName; @ApiModelProperty("邮箱") private String email; @ApiModelProperty("手机号码") private String phoneNumber; @ApiModelProperty("性别(0男 1女 2未知)") private String sex; @ApiModelProperty("头像") private String avatar; @ApiModelProperty("密码") private String password; @ApiModelProperty("状态(0正常 1停用)") private String status; @ApiModelProperty("岗位id") private List postIds; @ApiModelProperty("角色id") private List roleIds; } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysConfigMapper.java ================================================ package ginyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import ginyi.system.domain.SysConfig; /** * 系统配置表 */ public interface SysConfigMapper extends BaseMapper { } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysDeptMapper.java ================================================ package ginyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import ginyi.system.domain.SysDept; import ginyi.system.domain.model.dto.DeptDto; import org.apache.ibatis.annotations.Param; public interface SysDeptMapper extends BaseMapper { /** * 查询部门列表 * * @param deptDto * @param page */ public IPage list(@Param("deptDto") DeptDto deptDto, Page page); /** * 新增部门 * * @param deptDto */ public void insertDept(@Param("deptDto") DeptDto deptDto); /** * 更新部门 * * @param deptDto */ public void updateDept(@Param("deptDto") DeptDto deptDto); /** * 更新状态 * * @param deptDto */ public void updateDeptStatus(@Param("deptDto") DeptDto deptDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysLogininforMapper.java ================================================ package ginyi.system.mapper; import ginyi.system.domain.SysLogLogin; import java.util.List; /** * 系统访问日志情况信息 数据层 * * @author ruoyi */ public interface SysLogininforMapper { /** * 新增系统登录日志 * * @param logininfor 访问日志对象 */ public void insertLogininfor(SysLogLogin logininfor); /** * 查询系统登录日志集合 * * @param logininfor 访问日志对象 * @return 登录记录集合 */ public List selectLogininforList(SysLogLogin logininfor); /** * 批量删除系统登录日志 * * @param infoIds 需要删除的登录日志ID * @return 结果 */ public int deleteLogininforByIds(Long[] infoIds); /** * 清空系统登录日志 * * @return 结果 */ public int cleanLogininfor(); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysMenuMapper.java ================================================ package ginyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import ginyi.system.domain.SysMenu; import ginyi.system.domain.model.dto.MenuDto; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 菜单表 数据层 * * @author ruoyi */ public interface SysMenuMapper extends BaseMapper { /** * 根据用户ID查询权限 * * @param userId 用户ID * @return 权限列表 */ public List selectMenuPermsByUserId(Long userId); /** * 根据角色ID查询权限 * * @param roleId 角色ID * @return 权限列表 */ public List selectMenuPermsByRoleId(Long roleId); /** * 根据用户查询系统菜单列表 * * @return 菜单列表 */ public List selectMenuListByUserId(Long userId); /** * 管理员查询(管理)菜单列表 * @return */ public List selectMenuListByAdmin(@Param("menuDto") MenuDto menuDto); /** * 添加菜单 * @param menuDto * @return */ public boolean insertMenu(@Param("menuDto") MenuDto menuDto); /** * 更新菜单 * @param menuDto */ public void updateMenu(@Param("menuDto") MenuDto menuDto); /** * 更新状态 * @param menuDto */ public void updateMenuStatus(@Param("menuDto") MenuDto menuDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysNoticeMapper.java ================================================ package ginyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import ginyi.system.domain.SysNotice; import ginyi.system.domain.model.dto.NoticeDto; import ginyi.system.domain.model.vo.NoticeVo; import org.apache.ibatis.annotations.Param; public interface SysNoticeMapper extends BaseMapper { /** * 获取通知公告列表 */ public IPage list(@Param("noticeDto") NoticeDto noticeDto, Page page); /** * 发布通知公告 */ public void addNotice(@Param("noticeDto") NoticeDto noticeDto); /** * 获取用户通知公告列表 * @param userId * @param page * @return */ public IPage getUserNoticeList(Long userId, Page page); /** * 确认收到通知公告 * @param notice */ public void haveRead(@Param("notice") SysNotice notice); /** * 根据 noticeId 查询通知公告 * @param noticeId * @return */ public SysNotice selectOne(Long noticeId); /** * 更新通知公告 * @param noticeDto */ public void updateNotice(@Param("noticeDto") NoticeDto noticeDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysOperLogMapper.java ================================================ package ginyi.system.mapper; import ginyi.system.domain.SysLogOperation; import java.util.List; /** * 操作日志 数据层 * * @author ruoyi */ public interface SysOperLogMapper { /** * 新增操作日志 * * @param operLog 操作日志对象 */ public void insertOperlog(SysLogOperation operLog); /** * 查询系统操作日志集合 * * @param operLog 操作日志对象 * @return 操作日志集合 */ public List selectOperLogList(SysLogOperation operLog); /** * 批量删除系统操作日志 * * @param operIds 需要删除的操作日志ID * @return 结果 */ public int deleteOperLogByIds(Long[] operIds); /** * 查询操作日志详细 * * @param operId 操作ID * @return 操作日志对象 */ public SysLogOperation selectOperLogById(Long operId); /** * 清空操作日志 */ public void cleanOperLog(); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysPostMapper.java ================================================ package ginyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import ginyi.system.domain.SysPost; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.vo.PostVo; import org.apache.ibatis.annotations.Param; /** * 岗位 */ public interface SysPostMapper extends BaseMapper { /** * 查询岗位列表 * * @param postDto * @param page * @return */ public IPage list(@Param("postDto") PostDto postDto, Page page); /** * 新增岗位 * * @param postDto */ public void insertPost(@Param("postDto") PostDto postDto); /** * 更新岗位 * * @param postDto */ public void updatePost(@Param("postDto") PostDto postDto); /** * 更新状态 * @param postDto */ public void updatePostStatus(@Param("postDto") PostDto postDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysRoleMapper.java ================================================ package ginyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import ginyi.system.domain.SysRole; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.vo.RoleVo; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 角色表 数据层 * * @author ruoyi */ public interface SysRoleMapper extends BaseMapper { /** * 根据用户ID查询角色 * * @param userId 用户ID * @return 角色列表 */ public List selectRolePermissionByUserId(Long userId); /** * 查询角色列表 * * @param roleDto * @param page * @return */ public IPage list(@Param("roleDto") RoleDto roleDto, Page page); /** * 新增角色 * * @param roleDto */ public void insertRole(@Param("roleDto") RoleDto roleDto); /** * 新增角色对应的菜单权限 * * @param roleDto */ public void insertRoleMenu(@Param("roleDto") RoleDto roleDto); /** * 更新角色 * * @param roleDto */ public void updateRole(@Param("roleDto") RoleDto roleDto); /** * 更新角色对应的菜单权限 * * @param roleDto */ public void updateRoleMenu(@Param("roleDto") RoleDto roleDto); /** * 根据角色id查询角色详情 * @param roleId * @return */ public RoleVo selectRoleByRoleId(Long roleId); /** * 更新状态 * @param roleDto */ public void updateRoleStatus(@Param("roleDto") RoleDto roleDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/mapper/SysUserMapper.java ================================================ package ginyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.UserVo; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 用户表 */ public interface SysUserMapper extends BaseMapper { /** * 通过用户名查询用户 * * @param userName 用户名 * @return 用户对象信息 */ public SysUser selectUserByUserName(String userName); /** * 校验用户名称是否唯一 * * @param userName 用户名称 * @return 结果 */ public SysUser checkUserNameUnique(String userName); /** * 新增用户信息 * * @param userDto 用户信息 * @return 结果 */ public int insertUser(@Param("userDto") UserDto userDto); /** * 插入用户岗位中间表, 插入用户对应的岗位信息 * @param userDto * @return */ public int insertUserPostIds(@Param("userDto") UserDto userDto); /** * 插入用户角色中间表, 插入用户对应的角色信息 * @param userDto * @return */ public int insertUserRoleIds(@Param("userDto") UserDto userDto); /** * 修改用户信息 * * @param userDto 用户信息 * @return 结果 */ public int updateUser(@Param("userDto") UserDto userDto); /** * 更新用户岗位中间表, 更新用户对应的岗位信息 * @param userDto * @return */ public int updateUserPostIds(@Param("userDto") UserDto userDto); /** * 更新用户角色中间表, 更新用户对应的角色信息 * @param userDto * @return */ public int updateUserRoleIds(@Param("userDto") UserDto userDto); /** * 根据用户id查询用户 * @param userId * @return */ public UserVo selectUserByUserId(String userId); /** * 查询用户列表(不含admin) * @param userDto * @param page * @return */ public IPage list(@Param("userDto") UserDto userDto, Page page); /** * 更新用户状态 * @param userDto */ public void updateUserStatus(@Param("userDto") UserDto userDto); /** * 根据部门ids获取用户列表 * @param deptIds * @return */ public List selectUserByDeptIds(List deptIds); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysConfigService.java ================================================ package ginyi.system.service; public interface ISysConfigService { /** * 获取验证码开关 * * @return true开启,false关闭 */ public boolean selectCaptchaEnabled(); /** * 根据键名查询参数配置信息 * * @param configKey 参数key * @return 参数键值 */ public String selectConfigByKey(String configKey); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysDeptService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysDept; import ginyi.system.domain.model.dto.DeptDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.DeptVo; import java.util.Set; /** * 系统部门 */ public interface ISysDeptService { /** * 获取部门列表 * @param deptDto * @param page * @param pageSize */ public BaseVo list(DeptDto deptDto, Long page, Long pageSize); /** * 根据部门id获取部门详情 * @param deptId * @return */ public DeptVo getDeptByDeptId(Long deptId); /** * 新增部门 * @param deptDto */ public void addDept(DeptDto deptDto); /** * 更新部门 * @param deptDto */ public void updateDept(DeptDto deptDto); /** * 删除部门 * @param deptId */ public void removeDeptById(Long deptId); /** * 批量删除部门 * @param ids */ public void removeDeptByIds(Set ids); /** * 更改状态(0正常 1停用) * @param deptDto */ public void updateStatus(DeptDto deptDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysLogService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysLogLogin; import ginyi.system.domain.SysLogOperation; import ginyi.system.domain.model.vo.BaseVo; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; public interface ISysLogService { /** * 获取登录日志 * @param page * @param pageSize * @return */ public BaseVo getLoginLogList(Long page, Long pageSize); /** * 获取操作日志 * @param page * @param pageSize * @return */ public BaseVo getOperationLogList(Long page, Long pageSize); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysLoginService.java ================================================ package ginyi.system.service; import ginyi.system.domain.model.dto.LoginDto; import ginyi.system.domain.model.dto.RegisterDto; import ginyi.system.domain.model.vo.LoginVo; public interface ISysLoginService { /** * 登录验证 * @param loginDto * @return */ public LoginVo login(LoginDto loginDto); /** * 用户注册 * @param registerDto */ public void register(RegisterDto registerDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysLogininforService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysLogLogin; import java.util.List; /** * 系统访问日志情况信息 服务层 * * @author ruoyi */ public interface ISysLogininforService { /** * 新增系统登录日志 * * @param logininfor 访问日志对象 */ public void insertLogininfor(SysLogLogin logininfor); /** * 查询系统登录日志集合 * * @param logininfor 访问日志对象 * @return 登录记录集合 */ public List selectLogininforList(SysLogLogin logininfor); /** * 批量删除系统登录日志 * * @param infoIds 需要删除的登录日志ID * @return 结果 */ public int deleteLogininforByIds(Long[] infoIds); /** * 清空系统登录日志 */ public void cleanLogininfor(); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysMenuService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysMenu; import ginyi.system.domain.model.dto.MenuDto; import ginyi.system.domain.model.vo.BaseVo; import java.util.Set; /** * 菜单 业务层 * * @author ruoyi */ public interface ISysMenuService { /** * 根据角色ID查询权限 * * @param roleId 角色ID * @return 权限列表 */ public Set selectMenuPermsByRoleId(Long roleId); /** * 根据用户ID查询权限 * * @param userId 用户ID * @return 权限列表 */ public Set selectMenuPermsByUserId(Long userId); /** * 根据用户查询系统菜单列表 * * @return 菜单列表 */ public BaseVo selectMenuList(); /** * 管理员查询系统菜单列表 * * @return 菜单列表 */ public BaseVo list(MenuDto menuDto); /** * 添加菜单 * @param menuDto */ public void addMenu(MenuDto menuDto); /** * 根据id获取菜单详情 * @param menuId * @return */ public SysMenu getMenuById(Long menuId); /** * 更新菜单 * @param menuDto */ public void updateMenu(MenuDto menuDto); /** * 根据菜单id删除 * @param menuId */ public void removeMenuById(Long menuId); /** * 根据ids批量删除菜单 * @param ids */ public void removeMenuByIds(Set ids); /** * 更改状态(0正常 1停用) * @param menuDto */ public void updateStatus(MenuDto menuDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysMonitorService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysServer; import ginyi.system.domain.model.dto.CacheDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.CacheKeyVo; import ginyi.system.domain.model.vo.CacheVo; /** * 系统服务 */ public interface ISysMonitorService { /** * 获取系统服务信息,如 cpu、内存 等 */ public SysServer getServerInfo() throws InterruptedException; /** * 获取缓存列表 */ public BaseVo getCacheList(); /** * 获取缓存详情 */ public CacheVo getCacheDetails(CacheDto cacheDto); /** * 删除缓存 */ public void removeCache(String key); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysNoticeService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysNotice; import ginyi.system.domain.model.dto.NoticeDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.NoticeVo; /** * 通知公告 */ public interface ISysNoticeService { /** * 获取通知公告列表 * @param noticeDto * @param page * @param pageSize * @return */ public BaseVo list(NoticeDto noticeDto, Long page, Long pageSize); /** * 发布通知公告 * @param noticeDto */ public void add(NoticeDto noticeDto); /** * 获取用户通知公告列表 * @param page * @param pageSize * @return */ public BaseVo getUserNoticeList(Long page, Long pageSize); /** * 确认收到通知公告 * @param noticeId */ public void haveRead(Long noticeId); /** * 删除通知公告 * @param noticeId */ public void remove(Long noticeId); /** * 更新通知公告 * @param noticeDto */ public void updateNotice(NoticeDto noticeDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysOnlineService.java ================================================ package ginyi.system.service; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.SessionUserVo; import java.util.Set; public interface ISysOnlineService { /** * 当前在线用户列表 */ public BaseVo getOnlineUserList(Long page, Long pageSize); /** * 强制用户下线 * @param sessionId */ public void removeUser(String sessionId); /** * 批量强制用户下线 * @param ids */ public void removeUser(Set ids); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysOperLogService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysLogOperation; import java.util.List; /** * 操作日志 服务层 * * @author ruoyi */ public interface ISysOperLogService { /** * 新增操作日志 * * @param operLog 操作日志对象 */ public void insertOperlog(SysLogOperation operLog); /** * 查询系统操作日志集合 * * @param operLog 操作日志对象 * @return 操作日志集合 */ public List selectOperLogList(SysLogOperation operLog); /** * 批量删除系统操作日志 * * @param operIds 需要删除的操作日志ID * @return 结果 */ public int deleteOperLogByIds(Long[] operIds); /** * 查询操作日志详细 * * @param operId 操作ID * @return 操作日志对象 */ public SysLogOperation selectOperLogById(Long operId); /** * 清空操作日志 */ public void cleanOperLog(); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysPasswordService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysUser; public interface ISysPasswordService { public void validate(SysUser user); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysPermissionService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysUser; import java.util.Set; public interface ISysPermissionService { /** * 获取角色数据权限 * * @param user 用户信息 * @return 角色权限信息 */ public Set getRolePermission(SysUser user); /** * 获取菜单数据权限 * * @param user 用户信息 * @return 菜单权限信息 */ public Set getMenuPermission(SysUser user); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysPostService.java ================================================ package ginyi.system.service; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.PostVo; import java.util.Set; public interface ISysPostService { /** * 查询岗位列表 * * @param postDto * @param page * @param pageSize * @return */ public BaseVo list(PostDto postDto, Long page, Long pageSize); /** * 根据岗位id获取岗位详情 * * @param postId * @return */ public PostVo getPostByPostId(Long postId); /** * 添加岗位 * * @param postDto */ public void addPost(PostDto postDto); /** * 更新岗位 * * @param postDto */ public void updatePost(PostDto postDto); /** * 根据id删除岗位 * * @param postId */ public void removePostById(Long postId); /** * 批量删除岗位 * * @param ids */ void removeDeptByIds(Set ids); /** * 更改状态(0正常 1停用) * @param postDto */ public void updateStatus(PostDto postDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysRoleService.java ================================================ package ginyi.system.service; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.RoleVo; import java.util.Set; public interface ISysRoleService { /** * 根据用户ID查询角色权限 * * @param userId 用户ID * @return 权限列表 */ public Set selectRolePermissionByUserId(Long userId); /** * 角色列表 * * @param roleDto * @param page * @param pageSize * @return */ BaseVo list(RoleDto roleDto, Long page, Long pageSize); /** * 根据角色id获取角色 * * @param roleId * @return */ public RoleVo getRoleByRoleId(Long roleId); /** * 新增角色 * * @param roleDto */ public void addRole(RoleDto roleDto); /** * 跟新角色 * * @param roleDto */ public void updateRole(RoleDto roleDto); /** * 删除角色 * * @param roleId */ public void removeByRoleId(Long roleId); /** * 批量删除角色 * * @param ids */ public void removeByRoleIds(Set ids); /** * 更改状态(0正常 1停用) * @param roleDto */ public void updateStatus(RoleDto roleDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ISysUserService.java ================================================ package ginyi.system.service; import ginyi.system.domain.SysUser; import ginyi.system.domain.model.dto.DeptDto; import ginyi.system.domain.model.dto.PostDto; import ginyi.system.domain.model.dto.RoleDto; import ginyi.system.domain.model.dto.UserDto; import ginyi.system.domain.model.vo.BaseVo; import ginyi.system.domain.model.vo.UserVo; import java.util.HashMap; import java.util.Set; public interface ISysUserService { /** * 修改用户基本信息 * * @param userDto 用户信息 * @return 结果 */ public void updateUser(UserDto userDto); /** * 通过用户名查询用户 * * @param userName 用户名 * @return 用户对象信息 */ public SysUser selectUserByUserName(String userName); /** * 校验用户名称是否唯一 * * @param user 用户信息 * @return 结果 */ public String checkUserNameUnique(SysUser user); /** * 注册用户信息 * * @param user 用户信息 * @return 结果 */ public boolean registerUser(SysUser user); /** * 新增用户 * * @param userDto */ public void addUser(UserDto userDto); /** * 根据用户id查询用户 * @param userId */ public UserVo getUserByUserId(String userId); /** * 查询用户列表(不含admin) * @param userDto * @param page * @param pageSize * @return */ public BaseVo list(UserDto userDto, Long page, Long pageSize); /** * 根据用户id删除用户 * @param userId */ public void removeById(Long userId); /** * 根据userId批量删除用户 * @param ids */ public void removeUserByIds(Set ids); /** * 更改状态(0正常 1停用) * @param userDto */ public void updateStatus(UserDto userDto); /** * 根据部门 id 获取用户列表 * @param deptDto * @return */ public BaseVo> getUserListByDeptIds(DeptDto deptDto); /** * 根据岗位 id 获取用户列表 * @param postDto * @return */ public BaseVo> getUserListByPostIds(PostDto postDto); /** * 根据角色 id 获取用户列表 * @param roleDto * @return */ public BaseVo> getUserListByRoleIds(RoleDto roleDto); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/ITokenService.java ================================================ package ginyi.system.service; import ginyi.system.domain.LoginUser; import javax.servlet.http.HttpServletRequest; public interface ITokenService { /** * 创建令牌 * * @param loginUser 用户信息 * @return 令牌 */ public String createToken(LoginUser loginUser); /** * 获取用户身份信息 * * @return 用户信息 */ public LoginUser getLoginUser(HttpServletRequest request); /** * 删除用户身份信息 */ public void delLoginUser(String token); /** * 验证令牌有效期,相差不足20分钟,自动刷新缓存 * * @param loginUser * @return 令牌 */ public void verifyToken(LoginUser loginUser); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/java/ginyi/system/service/IVerifyService.java ================================================ package ginyi.system.service; public interface IVerifyService { /** * 图片验证码 */ public String captcha(); /** * 校验验证码 */ public void checkImgCode(String code); } ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/application.yml ================================================ # 项目应用配置 ginyi: # 项目应用作者 project-author: ginyi@aliyun.com # 项目应用名称 project-name: ginyi-springboot-demo # 项目应用版本 project-version: 1.0.0 # 项目应用描述 project-description: 重构ruoyi,学习优质项目,加油~ # 文件路径 示例( Windows配置D:/ginyi/uploadPath,Linux配置 /home/ginyi/uploadPath) profile: D:/ginyi/uploadPath # 应用服务 server: servlet: # 应用访问的路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 spring: # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB mvc: pathmatch: matching-strategy: ant_path_matcher # 数据源 datasource: type: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/ginyi-spring-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: 123456 # 初始连接数 initialSize: 5 # 最小连接池数量 minIdle: 10 # 最大连接池数量 maxActive: 20 testWhileIdle: true # redis 配置 redis: # 地址 host: 127.0.0.1 # 端口,默认为 6379 port: 6379 # 数据库索引 database: 0 # 密码 password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms data: mongodb: uri: mongodb://127.0.0.1:27017/ginyi-spring-vue # 资源信息 messages: # 国际化资源文件路径 basename: static/i18n/messages profiles: active: druid # 全局配置 @JsonFormat 时间格式化 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 # MyBatis-plus配置 mybatis-plus: # mapper.xml文件的位置 mapper-locations: classpath*:mapper/**/*Mapper.xml # 类型别名的位置 type-aliases-package: ginyi.**.domain configuration: # 开启下划线转驼峰 map-underscore-to-camel-case: true # 控制台输出sql语句 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: # 更改默认的 id 生成策略 id-type: assign_id # 逻辑删除 logic-delete-field: deleted logic-not-delete-value: 0 # 未删除 logic-delete-value: 1 # 已删除 # token配置 token: # 令牌自定义标识 header: Authorization # 令牌前缀 prefix: Bearer # 令牌密钥 secret: da5519948e0ac1db0ac8da59bb0770a1 # 令牌有效期(默认30分钟) expireTime: 30 # 用户配置 user: password: # 密码最大错误次数 maxRetryCount: 5 # 密码锁定时间(默认10分钟) lockTime: 10 # 接口文档的配置 swagger: # 联系方式 swagger-contact: name: Author Email is Ginyi@aliyun.com email: Ginyi@aliyun.com # 日志配置文件 logging: config: classpath:logging-config.xml ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/logging-config.xml ================================================ %d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %magenta([%21thread]) %green([%-50.50class]) >>> %cyan(%msg) %n ${LOG_PATH}/ginyi-springboot-%d{yyyy-MM-dd}.%i.log 100MB 15 ${LOG_PATTERN} ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/mapper/SysDeptMapper.xml ================================================ insert into sys_dept parent_id, dept_name, sort, leader, phone, email, `status`, create_by, create_time, update_by, update_time, 0, #{deptDto.parentId} #{deptDto.deptName}, #{deptDto.sort}, #{deptDto.leader}, #{deptDto.phone}, #{deptDto.email}, #{deptDto.status}, #{deptDto.createBy}, #{deptDto.createTime}, #{deptDto.updateBy}, #{deptDto.updateTime}, update sys_dept parent_id = #{deptDto.parentId}, dept_name = #{deptDto.deptName}, sort = #{deptDto.sort}, leader = #{deptDto.leader}, phone = #{deptDto.phone}, email = #{deptDto.email}, `status` = #{deptDto.status}, create_by = #{deptDto.createBy}, create_time = #{deptDto.createTime}, update_by = #{deptDto.updateBy}, update_time = #{deptDto.updateTime}, remark = #{deptDto.remark}, where dept_id = #{deptDto.deptId} update sys_dept `status` = #{deptDto.status}, update_by = #{deptDto.updateBy}, update_time = #{deptDto.updateTime}, where dept_id = #{deptDto.deptId} and deleted = '0' ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/mapper/SysMenuMapper.xml ================================================ select sm.menu_id, sm.menu_name, sm.parent_id, sm.sort, sm.name, sm.path, sm.component, sm.query, sm.is_frame, sm.is_cache, sm.menu_type, sm.visible, sm.status, sm.perms, sm.icon, sm.create_by, sm.create_time, sm.update_by, sm.update_time, sm.remark from sys_menu as sm insert into sys_menu ( menu_name, sort, menu_type, visible, status, component, path, query, is_frame, is_cache, perms, icon, create_by, create_time, update_by, update_time, parent_id ) value ( #{menuDto.menuName}, #{menuDto.sort}, #{menuDto.menuType}, #{menuDto.visible}, #{menuDto.status}, #{menuDto.component}, #{menuDto.path}, #{menuDto.query}, #{menuDto.isFrame}, #{menuDto.isCache}, #{menuDto.perms}, #{menuDto.icon}, #{menuDto.createBy}, #{menuDto.createTime}, #{menuDto.updateBy}, #{menuDto.updateTime}, 0 #{menuDto.parentId} ) update sys_menu menu_name = #{menuDto.menuName}, `name` = #{menuDto.name}, parent_id = #{menuDto.parentId}, sort = #{menuDto.sort}, `path` = #{menuDto.path}, component = #{menuDto.component}, `query` = #{menuDto.query}, is_frame = #{menuDto.isFrame}, is_cache = #{menuDto.isCache}, menu_type = #{menuDto.menuType}, `visible` = #{menuDto.visible}, `status` = #{menuDto.status}, perms = #{menuDto.perms}, icon = #{menuDto.icon}, create_by = #{menuDto.createBy}, create_time = #{menuDto.createTime}, update_by = #{menuDto.updateBy}, update_time = #{menuDto.updateTime}, remark = #{menuDto.remark}, where menu_id = #{menuDto.menuId} update sys_menu `status` = #{menuDto.status}, update_by = #{menuDto.updateBy}, update_time = #{menuDto.updateTime}, where menu_id = #{menuDto.menuId} and deleted = '0' ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/mapper/SysNoticeMapper.xml ================================================ insert into sys_notice title, type, content, `status`, user_ids, create_by, create_time, update_by, update_time, remark, #{noticeDto.title}, #{noticeDto.type}, #{noticeDto.content}, #{noticeDto.status}, json_array #{userId} , #{noticeDto.createBy}, #{noticeDto.createTime}, #{noticeDto.updateBy}, #{noticeDto.updateTime}, #{noticeDto.remark}, update sys_notice user_read_ids = json_array #{userId} where notice_id = #{notice.noticeId} update sys_notice title = #{noticeDto.title}, type = #{noticeDto.type}, content = #{noticeDto.content}, `status` = #{noticeDto.status}, user_ids = json_array #{userId} , update_by = #{noticeDto.updateBy}, update_time = #{noticeDto.updateTime}, remark = #{noticeDto.remark}, where notice_id = #{noticeDto.noticeId} ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/mapper/SysPostMapper.xml ================================================ insert into sys_post post_name, post_code, sort, `status`, create_by, create_time, update_by, update_time, remark, #{postDto.postName}, #{postDto.postCode}, #{postDto.sort}, #{postDto.status}, #{postDto.createBy}, #{postDto.createTime}, #{postDto.updateBy}, #{postDto.updateTime}, #{postDto.remark}, update sys_post post_name = #{postDto.postName}, post_code = #{postDto.postCode}, sort = #{postDto.sort}, `status` = #{postDto.status}, update_by = #{postDto.updateBy}, update_time = #{postDto.updateTime}, remark = #{postDto.remark}, where post_id = #{postDto.postId} update sys_post `status` = #{postDto.status}, update_by = #{postDto.updateBy}, update_time = #{postDto.updateTime}, where post_id = #{postDto.postId} and deleted = '0' ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/mapper/SysRoleMapper.xml ================================================ select distinct r.role_id, r.role_name, r.role_key, r.sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, r.status, r.deleted, r.create_time, r.remark from sys_role r left join sys_user_role ur on ur.role_id = r.role_id left join sys_user u on u.user_id = ur.user_id left join sys_dept d on u.dept_id = d.dept_id insert into sys_role role_name, role_key, sort, `status`, create_by, create_time, remark, #{roleDto.roleName}, #{roleDto.roleKey}, #{roleDto.sort}, #{roleDto.status}, #{roleDto.createBy}, #{roleDto.createTime}, #{roleDto.remark}, insert into sys_role_menu role_id, menu_id, #{roleDto.roleId}, json_array #{menuId} update sys_role role_name = #{roleDto.roleName}, role_key = #{roleDto.roleKey}, sort = #{roleDto.sort}, `status` = #{roleDto.status}, update_by = #{roleDto.updateBy}, update_time = #{roleDto.updateTime}, remark = #{roleDto.remark}, where role_id = #{roleDto.roleId} update sys_role_menu menu_id = json_array #{menuId} where role_id = #{roleDto.roleId} update sys_role `status` = #{roleDto.status}, update_by = #{roleDto.updateBy}, update_time = #{roleDto.updateTime}, where role_id = #{roleDto.roleId} and deleted = '0' ================================================ FILE: ginyi-springboot/ginyi-system/src/main/resources/mapper/SysUserMapper.xml ================================================ update sys_user dept_id = #{userDto.deptId}, user_name = #{userDto.userName}, nick_name = #{userDto.nickName}, email = #{userDto.email}, phone_number = #{userDto.phoneNumber}, sex = #{userDto.sex}, avatar = #{userDto.avatar}, `password` = #{userDto.password}, `status` = #{userDto.status}, login_ip = #{userDto.loginIp}, login_date = #{userDto.loginDate}, create_by = #{userDto.createBy}, create_time = #{userDto.createTime}, update_by = #{userDto.updateBy}, update_time = #{userDto.updateTime}, remark = #{userDto.remark}, where user_id = #{userDto.userId} insert into sys_user user_id, dept_id, user_name, nick_name, email, avatar, phone_number, sex, password, status, create_by, create_time, update_by, update_time, remark, #{userDto.userId}, #{userDto.deptId}, #{userDto.userName}, #{userDto.nickName}, #{userDto.email}, #{userDto.avatar}, #{userDto.phoneNumber}, #{userDto.sex}, #{userDto.password}, #{userDto.status}, #{userDto.createBy}, #{userDto.createTime}, #{userDto.updateBy}, #{userDto.updateTime}, #{userDto.remark}, insert into sys_user_post user_id, post_id #{userDto.userId}, json_array #{postId} insert into sys_user_role user_id, role_id #{userDto.userId}, json_array #{roleId} update sys_user_post post_id = json_array #{postId} where user_id = #{userDto.userId} update sys_user_role role_id = json_array #{roleId} where user_id = #{userDto.userId} update sys_user `status` = #{userDto.status}, update_by = #{userDto.updateBy}, update_time = #{userDto.updateTime}, where user_id = #{userDto.userId} and deleted = '0' ================================================ FILE: ginyi-springboot/pom.xml ================================================ 4.0.0 pom ${project.artifactId} com.ginyi ginyi-springboot 0.0.1-SNAPSHOT ginyi-demo ginyi-common ginyi-server ginyi-framework ginyi-system org.springframework.boot spring-boot-starter-parent 2.5.14 0.0.1-SNAPSHOT 1.8 2.5.14 2.9.2 2.0.9 5.8.5 1.6.2 3.1.0 1.18 1.1.23 3.4.2 1.18.24 2.2.5.RELEASE 1.2.76 8.0.30 2.2.2 2.7.3 0.9.1 2.9.0 3.12.0 1.21 2.0.16 3.0.1 6.4.3 com.github.oshi oshi-core ${oshi.version} cn.hutool hutool-all ${hutool.version} javax.servlet javax.servlet-api ${servlet.version} provided com.alibaba fastjson ${fastjson.version} org.springframework.boot spring-boot-starter-validation ${validation.version} org.fusesource.jansi jansi ${jansi.version} org.projectlombok lombok ${lombok.version} true org.springframework.boot spring-boot-starter-web ${spring-boot.version} org.springframework.boot spring-boot-starter-test ${spring-boot.version} test org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.boot spring-boot-starter-aop ${spring-boot.version} org.springframework.boot spring-boot-starter-data-mongodb ${spring-boot.version} org.springframework.boot spring-boot-starter-websocket ${spring-boot.version} io.jsonwebtoken jjwt ${jwt.version} eu.bitwalker UserAgentUtils ${bitwalker.version} org.springframework.boot spring-boot-starter-security ${spring-boot.version} org.apache.commons commons-lang3 ${commons-lang3.version} org.apache.commons commons-pool2 ${commons-pool2.version} io.springfox springfox-swagger2 ${swagger.version} io.springfox springfox-swagger-ui ${swagger.version} com.github.xiaoymin knife4j-spring-boot-starter 2.0.5 org.springframework.boot spring-boot-starter-aop ${spring-boot.version} com.alibaba druid-spring-boot-starter ${druid.version} mysql mysql-connector-java ${mysql.version} org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis.version} com.baomidou mybatis-plus-boot-starter ${mybatis-plus.version} org.springframework.boot spring-boot-starter-data-redis ${redis.version} com.alibaba.fastjson2 fastjson2 ${fastjson.version} com.sun.mail javax.mail ${email.version} ${project.name} src/main/resources **/*.* false src/main/java **/*.xml false org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} repackage maven-compiler-plugin 3.8.0 ${maven.compiler.target} ${maven.compiler.source} UTF-8 spring-milestones Spring Milestones https://repo.spring.io/milestone false spring-snapshots Spring Snapshots https://repo.spring.io/snapshot false spring-milestones Spring Milestones https://repo.spring.io/milestone false spring-snapshots Spring Snapshots https://repo.spring.io/snapshot false ================================================ FILE: ginyi-springboot/sql/ginyi-spring-vue-mongo.sql ================================================ [File too large to display: 33.2 MB] ================================================ FILE: ginyi-springboot/sql/ginyi-spring-vue-mysql.sql ================================================ /* Navicat Premium Data Transfer Source Server : MySQL8.0 Source Server Type : MySQL Source Server Version : 80027 Source Host : localhost:3306 Source Schema : ginyi-spring-vue Target Server Type : MySQL Target Server Version : 80027 File Encoding : 65001 Date: 16/09/2023 15:27:21 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for gen_table -- ---------------------------- DROP TABLE IF EXISTS `gen_table`; CREATE TABLE `gen_table` ( `table_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号', `table_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '表名称', `table_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '表描述', `sub_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '关联子表的表名', `sub_table_fk_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '子表关联的外键名', `class_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '实体类名称', `tpl_category` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'crud' COMMENT '使用的模板(crud单表操作 tree树表操作)', `package_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成包路径', `module_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成模块名', `business_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成业务名', `function_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成功能名', `function_author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '生成功能作者', `gen_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '生成代码方式(0zip压缩包 1自定义路径)', `gen_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '/' COMMENT '生成路径(不填默认项目路径)', `options` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '其它生成选项', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`table_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '代码生成业务表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of gen_table -- ---------------------------- -- ---------------------------- -- Table structure for gen_table_column -- ---------------------------- DROP TABLE IF EXISTS `gen_table_column`; CREATE TABLE `gen_table_column` ( `column_id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号', `table_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '归属表编号', `column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列名称', `column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列描述', `column_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '列类型', `java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA类型', `java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'JAVA字段名', `is_pk` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否主键(1是)', `is_increment` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否自增(1是)', `is_required` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否必填(1是)', `is_insert` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否为插入字段(1是)', `is_edit` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否编辑字段(1是)', `is_list` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否列表字段(1是)', `is_query` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否查询字段(1是)', `query_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'EQ' COMMENT '查询方式(等于、不等于、大于、小于、范围)', `html_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', `dict_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典类型', `sort` int NULL DEFAULT NULL COMMENT '排序', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`column_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '代码生成业务表字段' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of gen_table_column -- ---------------------------- -- ---------------------------- -- Table structure for sys_config -- ---------------------------- DROP TABLE IF EXISTS `sys_config`; CREATE TABLE `sys_config` ( `config_id` int NOT NULL AUTO_INCREMENT COMMENT '参数主键', `config_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '参数名称', `config_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '参数键名', `config_value` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '参数键值', `config_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'N' COMMENT '系统内置(Y是 N否)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`config_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '参数配置表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_config -- ---------------------------- INSERT INTO `sys_config` VALUES (1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow'); INSERT INTO `sys_config` VALUES (2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '初始化密码 123456'); INSERT INTO `sys_config` VALUES (3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '深色主题theme-dark,浅色主题theme-light'); INSERT INTO `sys_config` VALUES (4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '是否开启验证码功能(true开启,false关闭)'); INSERT INTO `sys_config` VALUES (5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', '2022-12-03 07:21:33', '', NULL, '是否开启注册用户功能(true开启,false关闭)'); -- ---------------------------- -- Table structure for sys_dept -- ---------------------------- DROP TABLE IF EXISTS `sys_dept`; CREATE TABLE `sys_dept` ( `dept_id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id', `parent_id` bigint NULL DEFAULT 0 COMMENT '父部门id', `ancestors` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '祖级列表', `dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '部门名称', `sort` int NULL DEFAULT 0 COMMENT '显示顺序', `leader` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '负责人', `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '联系电话', `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '邮箱', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '部门状态(0正常 1停用)', `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`dept_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1003 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '部门表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dept -- ---------------------------- INSERT INTO `sys_dept` VALUES (100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-05-28 20:39:45', NULL); INSERT INTO `sys_dept` VALUES (101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL); INSERT INTO `sys_dept` VALUES (102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL); INSERT INTO `sys_dept` VALUES (103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL); INSERT INTO `sys_dept` VALUES (104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL); INSERT INTO `sys_dept` VALUES (105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL); INSERT INTO `sys_dept` VALUES (106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL); INSERT INTO `sys_dept` VALUES (107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', '', NULL, NULL); INSERT INTO `sys_dept` VALUES (108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-02-17 22:32:20', NULL); INSERT INTO `sys_dept` VALUES (109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-05-28 18:01:25', NULL); -- ---------------------------- -- Table structure for sys_dict_data -- ---------------------------- DROP TABLE IF EXISTS `sys_dict_data`; CREATE TABLE `sys_dict_data` ( `dict_code` bigint NOT NULL AUTO_INCREMENT COMMENT '字典编码', `dict_sort` int NULL DEFAULT 0 COMMENT '字典排序', `dict_label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典标签', `dict_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典键值', `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典类型', `css_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '样式属性(其他样式扩展)', `list_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '表格回显样式', `is_default` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'N' COMMENT '是否默认(Y是 N否)', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`dict_code`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '字典数据表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict_data -- ---------------------------- INSERT INTO `sys_dict_data` VALUES (1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '性别男'); INSERT INTO `sys_dict_data` VALUES (2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '性别女'); INSERT INTO `sys_dict_data` VALUES (3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '性别未知'); INSERT INTO `sys_dict_data` VALUES (4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '显示菜单'); INSERT INTO `sys_dict_data` VALUES (5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '隐藏菜单'); INSERT INTO `sys_dict_data` VALUES (6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态'); INSERT INTO `sys_dict_data` VALUES (7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '停用状态'); INSERT INTO `sys_dict_data` VALUES (8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态'); INSERT INTO `sys_dict_data` VALUES (9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '停用状态'); INSERT INTO `sys_dict_data` VALUES (10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '默认分组'); INSERT INTO `sys_dict_data` VALUES (11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统分组'); INSERT INTO `sys_dict_data` VALUES (12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统默认是'); INSERT INTO `sys_dict_data` VALUES (13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统默认否'); INSERT INTO `sys_dict_data` VALUES (14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '通知'); INSERT INTO `sys_dict_data` VALUES (15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '公告'); INSERT INTO `sys_dict_data` VALUES (16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态'); INSERT INTO `sys_dict_data` VALUES (17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '关闭状态'); INSERT INTO `sys_dict_data` VALUES (18, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '其他操作'); INSERT INTO `sys_dict_data` VALUES (19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '新增操作'); INSERT INTO `sys_dict_data` VALUES (20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '修改操作'); INSERT INTO `sys_dict_data` VALUES (21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '删除操作'); INSERT INTO `sys_dict_data` VALUES (22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '授权操作'); INSERT INTO `sys_dict_data` VALUES (23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '导出操作'); INSERT INTO `sys_dict_data` VALUES (24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '导入操作'); INSERT INTO `sys_dict_data` VALUES (25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '强退操作'); INSERT INTO `sys_dict_data` VALUES (26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '生成操作'); INSERT INTO `sys_dict_data` VALUES (27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '清空操作'); INSERT INTO `sys_dict_data` VALUES (28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '正常状态'); INSERT INTO `sys_dict_data` VALUES (29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '停用状态'); -- ---------------------------- -- Table structure for sys_dict_type -- ---------------------------- DROP TABLE IF EXISTS `sys_dict_type`; CREATE TABLE `sys_dict_type` ( `dict_id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典主键', `dict_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典名称', `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '字典类型', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`dict_id`) USING BTREE, UNIQUE INDEX `dict_type`(`dict_type`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '字典类型表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_dict_type -- ---------------------------- INSERT INTO `sys_dict_type` VALUES (1, '用户性别', 'sys_user_sex', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '用户性别列表'); INSERT INTO `sys_dict_type` VALUES (2, '菜单状态', 'sys_show_hide', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '菜单状态列表'); INSERT INTO `sys_dict_type` VALUES (3, '系统开关', 'sys_normal_disable', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统开关列表'); INSERT INTO `sys_dict_type` VALUES (4, '任务状态', 'sys_job_status', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '任务状态列表'); INSERT INTO `sys_dict_type` VALUES (5, '任务分组', 'sys_job_group', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '任务分组列表'); INSERT INTO `sys_dict_type` VALUES (6, '系统是否', 'sys_yes_no', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '系统是否列表'); INSERT INTO `sys_dict_type` VALUES (7, '通知类型', 'sys_notice_type', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '通知类型列表'); INSERT INTO `sys_dict_type` VALUES (8, '通知状态', 'sys_notice_status', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '通知状态列表'); INSERT INTO `sys_dict_type` VALUES (9, '操作类型', 'sys_oper_type', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '操作类型列表'); INSERT INTO `sys_dict_type` VALUES (10, '系统状态', 'sys_common_status', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '登录状态列表'); -- ---------------------------- -- Table structure for sys_job -- ---------------------------- DROP TABLE IF EXISTS `sys_job`; CREATE TABLE `sys_job` ( `job_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务ID', `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '任务名称', `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名', `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '调用目标字符串', `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT 'cron执行表达式', `misfire_policy` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '3' COMMENT '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', `concurrent` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '1' COMMENT '是否并发执行(0允许 1禁止)', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '状态(0正常 1暂停)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '备注信息', PRIMARY KEY (`job_id`, `job_name`, `job_group`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 123 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '定时任务调度表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_job -- ---------------------------- INSERT INTO `sys_job` VALUES (1, '系统默认(无参)', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', '2022-12-03 07:21:33', '', '2022-12-07 08:22:56', ''); INSERT INTO `sys_job` VALUES (2, '系统默认(有参)', 'DEFAULT', 'ryTask.ryParams(\'ry\')', '0/15 * * * * ?', '3', '1', '1', 'admin', '2022-12-03 07:21:33', '', '2022-12-07 08:22:56', ''); INSERT INTO `sys_job` VALUES (3, '系统默认(多参)', 'DEFAULT', 'ryTask.ryMultipleParams(\'ry\', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', '2022-12-03 07:21:33', '', '2022-12-07 08:22:56', ''); -- ---------------------------- -- Table structure for sys_job_log -- ---------------------------- DROP TABLE IF EXISTS `sys_job_log`; CREATE TABLE `sys_job_log` ( `job_log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务日志ID', `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务名称', `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务组名', `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '调用目标字符串', `job_message` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '日志信息', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '执行状态(0正常 1失败)', `exception_info` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '异常信息', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`job_log_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '定时任务调度日志表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_job_log -- ---------------------------- -- ---------------------------- -- Table structure for sys_menu -- ---------------------------- DROP TABLE IF EXISTS `sys_menu`; CREATE TABLE `sys_menu` ( `menu_id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID', `menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '菜单名称', `parent_id` bigint NULL DEFAULT 0 COMMENT '父菜单ID', `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '路由name,用于跳转', `sort` int NULL DEFAULT 0 COMMENT '显示顺序', `path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '路由地址', `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '组件路径', `query` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '路由参数', `is_frame` int NULL DEFAULT 1 COMMENT '是否为外链(0是 1否)', `is_cache` int NULL DEFAULT 0 COMMENT '是否缓存(0缓存 1不缓存)', `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标志(0代表存在 2代表删除)', `menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)', `visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '菜单状态(0正常 1停用)', `perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '权限标识', `icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '#' COMMENT '菜单图标', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '备注', PRIMARY KEY (`menu_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2039 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '菜单权限表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_menu -- ---------------------------- INSERT INTO `sys_menu` VALUES (1, '系统管理', 0, 'system', 1, 'system', NULL, '', 1, 0, '0', 'M', '0', '0', '', 'AccessibilitySharp', 'admin', '2022-12-03 07:21:33', '', NULL, '系统管理目录'); INSERT INTO `sys_menu` VALUES (2, '系统监控', 0, 'sysmonitor', 2, 'sysmonitor', NULL, '', 1, 0, '0', 'M', '0', '0', '', 'PersonOutline', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-23 21:14:26', '系统监控目录'); INSERT INTO `sys_menu` VALUES (3, '系统工具', 0, 'systools', 3, 'systools', NULL, '', 1, 0, '0', 'M', '0', '0', '', 'BuildSharp', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-26 17:01:21', '系统工具目录'); INSERT INTO `sys_menu` VALUES (100, '用户管理', 1, 'user', 1, 'user', 'pages/system/user/index', '', 1, 0, '0', 'C', '0', '0', 'system:user:list', 'Duplicate', 'admin', '2022-12-03 07:21:33', '', NULL, '用户管理菜单'); INSERT INTO `sys_menu` VALUES (101, '角色管理', 1, 'role', 2, 'role', 'pages/system/role/index', '', 1, 0, '0', 'C', '0', '0', 'system:role:list', 'FishSharp', 'admin', '2022-12-03 07:21:33', '', NULL, '角色管理菜单'); INSERT INTO `sys_menu` VALUES (102, '菜单管理', 1, 'menu', 3, 'menu', 'pages/system/menu/index', '', 1, 0, '0', 'C', '0', '0', 'system:menu:list', 'Heart', 'admin', '2022-12-03 07:21:33', '', NULL, '菜单管理菜单'); INSERT INTO `sys_menu` VALUES (103, '部门管理', 1, 'department', 4, 'department', 'pages/system/department/index', '', 1, 0, '0', 'C', '0', '0', 'system:dept:list', 'LayersSharp', 'admin', '2022-12-03 07:21:33', '', NULL, '部门管理菜单'); INSERT INTO `sys_menu` VALUES (104, '岗位管理', 1, 'position', 5, 'position', 'pages/system/position/index', '', 1, 0, '0', 'C', '0', '0', 'system:post:list', 'PawSharp', 'admin', '2022-12-03 07:21:33', '', NULL, '岗位管理菜单'); INSERT INTO `sys_menu` VALUES (105, '字典管理', 1, 'dict', 6, 'dict', 'pages/system/dict/index', '', 1, 0, '0', 'C', '0', '0', 'system:dict:list', 'LogoElectron', 'admin', '2022-12-03 07:21:33', '', NULL, '字典管理菜单'); INSERT INTO `sys_menu` VALUES (106, '参数设置', 1, 'params', 7, 'params', 'pages/system/params/index', '', 1, 0, '0', 'C', '0', '0', 'system:config:list', 'LogoWechat', 'admin', '2022-12-03 07:21:33', '', NULL, '参数设置菜单'); INSERT INTO `sys_menu` VALUES (107, '通知公告', 1, 'notice', 8, 'notice', 'pages/system/notice/index', '', 1, 0, '0', 'C', '0', '0', 'system:notice:list', 'MailUnreadOutline', 'admin', '2022-12-03 07:21:33', '', NULL, '通知公告菜单'); INSERT INTO `sys_menu` VALUES (108, '日志管理', 1, 'log', 9, 'log', '', '', 1, 0, '0', 'M', '0', '0', '', 'Mic', 'admin', '2022-12-03 07:21:33', '', NULL, '日志管理菜单'); INSERT INTO `sys_menu` VALUES (109, '在线用户', 2, 'online', 1, 'online', 'pages/monitor/online/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:online:list', 'Disc', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-23 21:14:13', '在线用户菜单'); INSERT INTO `sys_menu` VALUES (110, '定时任务', 2, 'task', 2, 'task', 'pages/monitor/task/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:job:list', 'ExtensionPuzzleSharp', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-26 16:59:34', '定时任务菜单'); INSERT INTO `sys_menu` VALUES (111, '数据监控', 2, 'data', 3, 'data', 'pages/monitor/data/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:druid:list', 'FilterCircle', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 14:02:06', '数据监控菜单'); INSERT INTO `sys_menu` VALUES (112, '服务监控', 2, 'service', 4, 'service', 'pages/monitor/service/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:server:list', 'FootballSharp', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 14:02:07', '服务监控菜单'); INSERT INTO `sys_menu` VALUES (113, '缓存监控', 2, 'cache', 5, 'cache', 'pages/monitor/cache/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:cache:list', 'Leaf', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 14:02:08', '缓存监控菜单'); INSERT INTO `sys_menu` VALUES (114, '缓存列表', 2, 'cacheList', 6, 'cacheList', 'pages/monitor/cacheList/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:cache:list', 'LogoOctocat', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-06-30 17:39:42', '缓存列表菜单'); INSERT INTO `sys_menu` VALUES (116, '代码生成', 3, 'code', 2, 'code', 'pages/systools/code/index', '', 1, 0, '0', 'C', '0', '0', 'tool:gen:list', 'Mail', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-26 17:05:52', '代码生成菜单'); INSERT INTO `sys_menu` VALUES (500, '操作日志', 108, 'operationLog', 1, 'operationLog', 'pages/system/log/operation/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:operlog:list', 'Notifications', 'admin', '2022-12-03 07:21:33', '', NULL, '操作日志菜单'); INSERT INTO `sys_menu` VALUES (501, '登录日志', 108, 'loginLog', 2, 'loginLog', 'pages/system/log/login/index', '', 1, 0, '0', 'C', '0', '0', 'monitor:loginlog:list', 'Planet', 'admin', '2022-12-03 07:21:33', '', NULL, '登录日志菜单'); INSERT INTO `sys_menu` VALUES (1000, '用户查询', 100, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:list', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1001, '用户新增', 100, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1002, '用户修改', 100, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1003, '用户删除', 100, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1004, '用户导出', 100, NULL, 5, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-02-19 15:38:58', ''); INSERT INTO `sys_menu` VALUES (1005, '用户导入', 100, NULL, 6, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:import', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1006, '重置密码', 100, NULL, 7, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:user:resetPwd', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1007, '角色查询', 101, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1008, '角色新增', 101, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1009, '角色修改', 101, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1010, '角色删除', 101, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1011, '角色导出', 101, NULL, 5, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:role:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1012, '菜单查询', 102, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1013, '菜单新增', 102, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1014, '菜单修改', 102, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1015, '菜单删除', 102, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:menu:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1016, '部门查询', 103, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:list', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1017, '部门新增', 103, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1018, '部门修改', 103, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1019, '部门删除', 103, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:dept:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1020, '岗位查询', 104, NULL, 1, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1021, '岗位新增', 104, NULL, 2, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1022, '岗位修改', 104, NULL, 3, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1023, '岗位删除', 104, NULL, 4, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1024, '岗位导出', 104, NULL, 5, '', '', '', 1, 0, '0', 'F', '1', '0', 'system:post:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1025, '字典查询', 105, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1026, '字典新增', 105, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1027, '字典修改', 105, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1028, '字典删除', 105, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1029, '字典导出', 105, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:dict:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1030, '参数查询', 106, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1031, '参数新增', 106, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1032, '参数修改', 106, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1033, '参数删除', 106, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1034, '参数导出', 106, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:config:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1035, '公告查询', 107, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1036, '公告新增', 107, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1037, '公告修改', 107, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1038, '公告删除', 107, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'system:notice:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1039, '操作查询', 500, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:operlog:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1040, '操作删除', 500, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:operlog:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1041, '日志导出', 500, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:operlog:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1042, '登录查询', 501, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1043, '登录删除', 501, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1044, '日志导出', 501, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1045, '账户解锁', 501, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:loginlog:unlock', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1046, '在线查询', 109, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:online:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1047, '批量强退', 109, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:online:batchLogout', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1048, '单条强退', 109, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:online:forceLogout', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1049, '任务查询', 110, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1050, '任务新增', 110, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:add', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1051, '任务修改', 110, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1052, '任务删除', 110, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1053, '状态修改', 110, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:changeStatus', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1054, '任务导出', 110, NULL, 6, '#', '', '', 1, 0, '0', 'F', '1', '0', 'monitor:job:export', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1055, '生成查询', 116, NULL, 1, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:query', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1056, '生成修改', 116, NULL, 2, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:edit', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1057, '生成删除', 116, NULL, 3, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:remove', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1058, '导入代码', 116, NULL, 4, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:import', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1059, '预览代码', 116, NULL, 5, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:preview', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (1060, '生成代码', 116, NULL, 6, '#', '', '', 1, 0, '0', 'F', '1', '0', 'tool:gen:code', 'PersonOutline', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_menu` VALUES (2031, '订单管理', 0, 'order', 4, 'order', '', NULL, 1, 0, '0', 'M', '0', '0', '', 'BarChartSharp', 'Ginyi', '2023-02-28 21:19:38', 'Ginyi', '2023-02-28 21:20:28', ''); INSERT INTO `sys_menu` VALUES (2032, '订单列表', 2031, 'orderList', 1, 'orderList', 'pages/order/index', NULL, 1, 0, '0', 'C', '0', '0', 'order:list', 'Card', 'Ginyi', '2023-02-28 21:22:53', 'Ginyi', '2023-02-28 21:28:47', ''); INSERT INTO `sys_menu` VALUES (2033, '商品管理', 0, 'product', 5, 'product', NULL, NULL, 1, 0, '0', 'M', '0', '0', NULL, 'Cart', 'Ginyi', '2023-02-28 21:35:33', 'Ginyi', '2023-05-28 20:39:38', ''); INSERT INTO `sys_menu` VALUES (2034, '商品列表', 2033, 'productList', 1, 'productList', 'pages/product/index', NULL, 1, 0, '0', 'C', '0', '0', 'product:list', 'Documents', 'Ginyi', '2023-02-28 21:37:50', 'Ginyi', '2023-05-28 18:01:16', ''); INSERT INTO `sys_menu` VALUES (2035, '查看缓存详情', 114, NULL, 0, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'monitor:cache:details', '#', 'Ginyi', '2023-06-30 17:35:50', '', NULL, ''); INSERT INTO `sys_menu` VALUES (2036, '删除缓存', 114, NULL, 2, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'monitor:cache:remove', '#', 'Ginyi', '2023-06-30 17:37:58', '', NULL, ''); INSERT INTO `sys_menu` VALUES (2037, '已阅公告', 107, NULL, 5, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'system:notice:haveRead', '#', 'Ginyi', '2023-07-09 00:31:01', '', NULL, ''); INSERT INTO `sys_menu` VALUES (2038, '公告查询(管理员)', 107, NULL, 0, '', NULL, NULL, 1, 0, NULL, 'F', '0', '0', 'system:notice:query:admin', '#', 'Ginyi', '2023-07-09 00:32:29', '', NULL, ''); -- ---------------------------- -- Table structure for sys_notice -- ---------------------------- DROP TABLE IF EXISTS `sys_notice`; CREATE TABLE `sys_notice` ( `notice_id` int NOT NULL AUTO_INCREMENT COMMENT '公告ID', `title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告标题', `type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告类型(0通知 1公告)', `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '公告内容', `user_ids` json NOT NULL COMMENT '通知的用户', `user_read_ids` json NULL COMMENT '已阅的用户', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '公告状态(0正常 1关闭)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`notice_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '通知公告表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_notice -- ---------------------------- INSERT INTO `sys_notice` VALUES (1, '温馨提醒:2018-07-01 若依新版本发布啦', '2', '新版本内容', '[1, 2]', '[1, 2]', '0', 'admin', '2022-12-03 07:21:33', '', NULL, NULL); INSERT INTO `sys_notice` VALUES (2, '维护通知:2018-07-01 若依系统凌晨维护', '1', '维护内容', '[1, 2]', '[2]', '0', 'admin', '2022-12-03 07:21:33', '', NULL, NULL); INSERT INTO `sys_notice` VALUES (14, '测试标题', '1', '测试标题内容', '[1]', NULL, '0', 'admin', '2023-07-02 18:42:14', '', NULL, NULL); -- ---------------------------- -- Table structure for sys_post -- ---------------------------- DROP TABLE IF EXISTS `sys_post`; CREATE TABLE `sys_post` ( `post_id` bigint NOT NULL AUTO_INCREMENT COMMENT '岗位ID', `post_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '岗位编码', `post_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '岗位名称', `sort` int NOT NULL COMMENT '显示顺序', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '状态(0正常 1停用)', `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '删除标志(0代表存在 2代表删除)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`post_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '岗位信息表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_post -- ---------------------------- INSERT INTO `sys_post` VALUES (1, 'ceo', '董事长', 1, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_post` VALUES (2, 'se', '项目经理', 2, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_post` VALUES (3, 'hr', '人力资源', 3, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_post` VALUES (4, 'user', '普通员工', 4, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, ''); INSERT INTO `sys_post` VALUES (9, 'test', '测试测试', 5, '0', '0', 'ginyi', '2022-12-31 18:23:34', 'Ginyi', '2023-06-28 22:04:13', NULL); -- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID', `role_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色名称', `role_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色权限字符串', `sort` int NOT NULL COMMENT '显示顺序', `data_scope` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', `menu_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '菜单树选择项是否关联显示', `dept_check_strictly` tinyint(1) NULL DEFAULT 1 COMMENT '部门树选择项是否关联显示', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色状态(0正常 1停用)', `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`role_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 113 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色信息表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_role -- ---------------------------- INSERT INTO `sys_role` VALUES (1, '超级管理员', 'admin', 1, '1', 1, 1, '0', '0', 'admin', '2022-12-03 07:21:33', '', NULL, '超级管理员'); INSERT INTO `sys_role` VALUES (2, '系统管理员', 'system', 1, '2', 1, 1, '0', '0', 'admin', '2022-12-03 07:21:33', 'Ginyi', '2023-07-09 00:40:25', '专门管理系统的'); INSERT INTO `sys_role` VALUES (111, '订单管理员', 'order', 0, '1', 1, 1, '0', '0', 'Ginyi', '2023-02-26 16:05:39', 'Ginyi', '2023-02-28 21:33:53', '专门管理订单的'); INSERT INTO `sys_role` VALUES (112, '商品管理员', 'conduct', 2, '1', 1, 1, '0', '0', 'Ginyi', '2023-02-26 16:06:28', 'Ginyi', '2023-05-28 20:39:27', '专门管理商品的'); -- ---------------------------- -- Table structure for sys_role_dept -- ---------------------------- DROP TABLE IF EXISTS `sys_role_dept`; CREATE TABLE `sys_role_dept` ( `role_id` bigint NOT NULL COMMENT '角色ID', `dept_id` bigint NOT NULL COMMENT '部门ID', PRIMARY KEY (`role_id`, `dept_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色和部门关联表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_role_dept -- ---------------------------- INSERT INTO `sys_role_dept` VALUES (2, 100); INSERT INTO `sys_role_dept` VALUES (2, 101); INSERT INTO `sys_role_dept` VALUES (2, 105); -- ---------------------------- -- Table structure for sys_role_menu -- ---------------------------- DROP TABLE IF EXISTS `sys_role_menu`; CREATE TABLE `sys_role_menu` ( `role_id` bigint NOT NULL COMMENT '角色ID', `menu_id` json NULL COMMENT '菜单ID', PRIMARY KEY (`role_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色和菜单关联表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_role_menu -- ---------------------------- INSERT INTO `sys_role_menu` VALUES (2, '[1, 100, 101, 102, 103, 104, 105, 106, 107, 108, 500, 501, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 2, 109, 1046, 1047, 1048, 110, 1049, 1050, 1051, 1052, 1053, 1054, 111, 112, 113, 114, 3, 116, 1055, 1056, 1057, 1058, 1059, 1060, 2035, 2036, 2037, 2038]'); INSERT INTO `sys_role_menu` VALUES (111, '[2031, 2032]'); INSERT INTO `sys_role_menu` VALUES (112, '[2033, 2034]'); -- ---------------------------- -- Table structure for sys_user -- ---------------------------- DROP TABLE IF EXISTS `sys_user`; CREATE TABLE `sys_user` ( `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', `dept_id` bigint NULL DEFAULT NULL COMMENT '部门ID', `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户账号', `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户昵称', `user_type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '00' COMMENT '用户类型(00系统用户)', `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '用户邮箱', `phone_number` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '手机号码', `sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)', `avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '头像地址', `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '密码', `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', `deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', `login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '最后登录IP', `login_date` datetime NULL DEFAULT NULL COMMENT '最后登录时间', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', PRIMARY KEY (`user_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 148 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_user -- ---------------------------- INSERT INTO `sys_user` VALUES (1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '192.168.0.101', '2023-07-09 01:06:17', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-07-09 01:06:17', '管理员'); INSERT INTO `sys_user` VALUES (2, 105, 'Ginyi', '(´・_・`)', '00', 'Ginyi@aliyun.com', '15666666666', '1', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '192.168.0.103', '2023-09-16 15:14:49', 'admin', '2022-12-03 07:21:32', 'Ginyi', '2023-09-16 15:14:49', '测试员'); INSERT INTO `sys_user` VALUES (133, 101, 'order', '订单管理员', '00', '', '13800138000', '0', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '127.0.0.1', '2023-03-02 23:06:23', 'ginyi', '2022-12-23 07:40:55', 'Ginyi', '2023-03-02 23:06:23', NULL); INSERT INTO `sys_user` VALUES (134, 104, 'product', '商品管理员', '00', '', '13800138000', '0', '', '$2a$10$0zqV6SsWmLkjBroBlzgnVO/mNRNTjbJrrCbUhRPOKZa9vyLnyJAR6', '0', '0', '127.0.0.1', '2023-05-27 22:38:58', 'ginyi', '2022-12-23 07:41:02', 'Ginyi', '2023-05-28 17:04:05', NULL); INSERT INTO `sys_user` VALUES (138, 106, 'tony', '测试用户3', '00', '', '13800138000', '0', '', '', '0', '0', '', NULL, 'ginyi', '2022-12-23 07:41:28', 'admin', '2023-03-03 21:50:14', '没有初始化密码,不能登录'); INSERT INTO `sys_user` VALUES (139, 109, 'lucy', '测试用户4', '00', '', '13800138000', '0', '', '', '1', '0', '', NULL, 'ginyi', '2022-12-23 13:31:22', 'Ginyi', '2023-07-09 13:20:26', '没有初始化密码,不能登录'); INSERT INTO `sys_user` VALUES (140, 101, 'carry', '测试用户5', '00', '', '', '0', '', '', '0', '0', '', NULL, 'ginyi', '2022-12-23 21:35:13', 'Ginyi', '2023-05-28 20:35:40', '没有初始化密码,不能登录'); INSERT INTO `sys_user` VALUES (145, 109, 'test6', '测试用户6', '00', '', '', '0', '', '', '0', '0', '', NULL, 'Ginyi', '2023-02-26 16:40:51', 'Ginyi', '2023-07-09 13:20:24', '没有初始化密码,不能登录'); -- ---------------------------- -- Table structure for sys_user_post -- ---------------------------- DROP TABLE IF EXISTS `sys_user_post`; CREATE TABLE `sys_user_post` ( `user_id` bigint NOT NULL COMMENT '用户ID', `post_id` json NOT NULL COMMENT '岗位ID', PRIMARY KEY (`user_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户与岗位关联表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_user_post -- ---------------------------- INSERT INTO `sys_user_post` VALUES (1, '[1]'); INSERT INTO `sys_user_post` VALUES (2, '[2]'); INSERT INTO `sys_user_post` VALUES (133, '[4]'); INSERT INTO `sys_user_post` VALUES (134, '[4]'); INSERT INTO `sys_user_post` VALUES (138, '[2, 4]'); INSERT INTO `sys_user_post` VALUES (139, '[1, 2]'); INSERT INTO `sys_user_post` VALUES (140, '[2, 9]'); INSERT INTO `sys_user_post` VALUES (145, '[9]'); -- ---------------------------- -- Table structure for sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `user_id` bigint NOT NULL COMMENT '用户ID', `role_id` json NOT NULL COMMENT '角色ID', PRIMARY KEY (`user_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户和角色关联表' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_user_role -- ---------------------------- INSERT INTO `sys_user_role` VALUES (1, '[1]'); INSERT INTO `sys_user_role` VALUES (2, '[2]'); INSERT INTO `sys_user_role` VALUES (133, '[111, 112]'); INSERT INTO `sys_user_role` VALUES (134, '[112]'); INSERT INTO `sys_user_role` VALUES (138, '[111]'); INSERT INTO `sys_user_role` VALUES (139, '[112]'); INSERT INTO `sys_user_role` VALUES (140, '[111]'); INSERT INTO `sys_user_role` VALUES (145, '[112]'); SET FOREIGN_KEY_CHECKS = 1; ================================================ FILE: ginyi-vue3/.editorconfig ================================================ root = true [*] charset = utf-8 indent_size = 4 ================================================ FILE: ginyi-vue3/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* node_modules dist dist-ssr *.local # Editor directories and files .vscode/* !.vscode/extensions.json .idea .DS_Store *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: ginyi-vue3/README.md ================================================ ### 温馨提示 1. `Ginyi-vue3`是一个`Vue3`的项目,如果你不了解`Vue3`,可以去看看其他框架。 2. `Ginyi-vue3`全量使用`script-return`,如果你不喜欢它,可以去看看其他框架。 ### 项目地址 演示地址:http://114.132.120.190:3800 (`仅PC端`) 代码仓库:https://gitee.com/Ginyi/ginyi-spring-vue ### 帮助文档 - 没有~不想写文档...如果`Star`数量破`1k`,可以考虑一下! ### 项目特色 - 界面简洁、舒适! - 动态菜单,粒子化的权限控制! - 不使用代码规范插件,代码灵活不受约束,如需要可自行添加! - 丰富的题主配置,内置多款主题色可供选择,同时可切换亮色/暗色模式! - 使用最新技术栈`Vite4 + Vue3 + TypeScript + Pinia`,搭配优秀`Vue3`专用组件库`Naive UI`! - 二次封装常用组件:`CommonTable、CommonForm、CommonModal`强强联合,15分钟搞定一套CURD! - 提供多个自定义指令:`v-focus、v-draggable、v-permission`等,同时提供多个常用数据字典:`是否、正常禁用、性别`等,减少重复代码! ### 前置知识 **重点:**使用本项目时,你需要具备以下基础知识: 1. `Vue3`语法基础,可以不熟悉` ================================================ FILE: ginyi-vue3/package.json ================================================ { "name": "ginyi-vue3", "private": true, "version": "1.0.0", "type": "module", "web-types": "web-types.json", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "@types/node": "^18.11.18", "@vicons/ionicons5": "^0.12.0", "@vitejs/plugin-vue-jsx": "^3.0.0", "@vueuse/core": "^9.10.0", "axios": "^1.2.2", "less": "^4.1.3", "less-loader": "^11.1.0", "mitt": "^3.0.0", "monaco-editor": "^0.36.0", "naive-ui": "^2.34.3", "pinia": "^2.0.28", "vue": "^3.2.45", "vue-router": "^4.1.6", "vuedraggable": "^4.1.0" }, "devDependencies": { "@vitejs/plugin-vue": "^4.0.0", "typescript": "^4.9.3", "vite": "^4.0.0", "vue-tsc": "^1.0.11" } } ================================================ FILE: ginyi-vue3/src/App.vue ================================================ ================================================ FILE: ginyi-vue3/src/api/controller/deptController.ts ================================================ import useRequest from "@/api/useRequest"; import {IPage} from "@/interface/modules/system"; export class deptController { /** * 获取部门列表 * @param data * @param pagination */ static list(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/dept/list", method: "post", params: pagination, data }) } /** * 获取部门详情 * @param deptId */ static getDeptDetailsById(deptId: number | string): Promise { return useRequest({ url: `/api/dept/getDeptByDeptId/${deptId}`, method: "get" }) } /** * 新增部门 * @param data */ static add(data: any): Promise { return useRequest({ url: "/api/dept/add", method: "post", data }) } /** * 更新部门 * @param data */ static edit(data: any): Promise { return useRequest({ url: "/api/dept/update", method: "post", data }) } /** * 删除部门 * @param deptId */ static deleteById(deptId: number | string): Promise { return useRequest({ url: `/api/dept/delete/${deptId}`, method: "post" }) } /** * 批量删除部门 * @param data */ static deleteByIds(data: Array): Promise { return useRequest({ url: "/api/dept/delete", method: "post", data }) } /** * 更新部门状态 * @param data */ static updateStatus(data: any): Promise { return useRequest({ url: "/api/dept/updateStatus", method: "post", data }) } } ================================================ FILE: ginyi-vue3/src/api/controller/logController.ts ================================================ import useRequest from "@/api/useRequest"; import {IPage} from "@/interface/modules/system"; export class logController { /** * 获取操作日志 * @param data * @param pagination */ static getOperationLogList(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/log/getOperationLogList", method: "get", params: pagination, }) } /** * 获取登录日志 * @param data * @param pagination */ static getLoginLogList(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/log/getLoginLogList", method: "get", params: pagination, }) } } ================================================ FILE: ginyi-vue3/src/api/controller/menuController.ts ================================================ import useRequest from "@/api/useRequest"; import {IPage} from "@/interface/modules/system"; export class menuController { /** * 获取路由列表 */ static getRouterList(): Promise { return useRequest({ url: "/api/menu/getRouterList", method: "get", }) } /** * 获取菜单列表 * @param data * @param pagination */ static list(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/menu/list", method: "post", params: pagination, data }) } /** * 获取部门详情 * @param menuId */ static getMenuDetailsById(menuId: number | string): Promise { return useRequest({ url: `/api/menu/getMenuByMenuId/${menuId}`, method: "get" }) } /** * 新增部门 * @param data */ static add(data: any): Promise { return useRequest({ url: "/api/menu/add", method: "post", data }) } /** * 更新部门 * @param data */ static edit(data: any): Promise { return useRequest({ url: "/api/menu/update", method: "post", data }) } /** * 删除部门 * @param menuId */ static deleteById(menuId: number | string): Promise { return useRequest({ url: `/api/menu/delete/${menuId}`, method: "post" }) } /** * 批量删除菜单 * @param data */ static deleteByIds(data: Array): Promise { return useRequest({ url: "/api/menu/delete", method: "post", data }) } /** * 更新菜单状态 * @param data */ static updateStatus(data: any): Promise { return useRequest({ url: "/api/menu/updateStatus", method: "post", data }) } } ================================================ FILE: ginyi-vue3/src/api/controller/monitorController.ts ================================================ import useRequest from "@/api/useRequest"; export class monitorController { /** * 获取服务器信息 */ static getServerInfo(): Promise { return useRequest({ url: "/api/monitor/getServerInfo", method: "get", }) } /** * 获取缓存列表 */ static getCacheList(): Promise { return useRequest({ url: "/api/monitor/getCacheList", method: "get", }) } /** * 获取缓存数据 */ static getCacheValue(data: any): Promise { return useRequest({ url: "/api/monitor/getCacheValue", method: "post", data: data }) } /** * 删除缓存 */ static removeCache(key: string): Promise { return useRequest({ url: `/api/monitor/removeCache/${key}`, method: "post", }) } } ================================================ FILE: ginyi-vue3/src/api/controller/onlineController.ts ================================================ import useRequest from "@/api/useRequest"; import {IPage} from "@/interface/modules/system"; export class onlineController { /** * 获取在线用户 * @param data * @param pagination */ static getOnlineUserList(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/online/getOnlineUserList", method: "get", params: pagination, }) } /** * 强制退出 * @param token */ static removeUser(token: any): Promise { return useRequest({ url: `/api/online/removeUser/${token}`, method: "post", }) } /** * 批量强制退出 * @param data */ static batchRemoveUser(data: any): Promise { return useRequest({ url: "/api/online/removeUser", method: "post", data }) } } ================================================ FILE: ginyi-vue3/src/api/controller/postController.ts ================================================ import useRequest from "@/api/useRequest"; import {IPage} from "@/interface/modules/system"; export class postController { /** * 获取岗位列表 * @param data * @param pagination */ static list(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/post/list", method: "post", params: pagination, data }) } /** * 获取岗位详情 * @param postId */ static getPostDetailsById(postId: number | string): Promise { return useRequest({ url: `/api/post/getPostByRoleId/${postId}`, method: "get" }) } /** * 新增岗位 * @param data * @param pagination */ static add(data: any): Promise { return useRequest({ url: "/api/post/add", method: "post", data }) } /** * 更新岗位 * @param data */ static edit(data: any): Promise { return useRequest({ url: "/api/post/update", method: "post", data }) } /** * 删除岗位 * @param userId */ static deleteById(userId: number | string): Promise { return useRequest({ url: `/api/post/delete/${userId}`, method: "post" }) } /** * 批量删除岗位 * @param data */ static deleteByIds(data: Array): Promise { return useRequest({ url: "/api/post/delete", method: "post", data }) } /** * 更新岗位状态 * @param data */ static updateStatus(data: any): Promise { return useRequest({ url: "/api/post/updateStatus", method: "post", data }) } } ================================================ FILE: ginyi-vue3/src/api/controller/roleController.ts ================================================ import useRequest from "@/api/useRequest"; import {IPage} from "@/interface/modules/system"; export class roleController { /** * 获取角色列表 * @param data * @param pagination */ static list(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/role/list", method: "post", params: pagination, data }) } /** * 获取角色详情 * @param roleId */ static getRoleDetailsById(roleId: number | string): Promise { return useRequest({ url: `/api/role/getRoleByRoleId/${roleId}`, method: "get" }) } /** * 新增角色 * @param data * @param pagination */ static add(data: any): Promise { return useRequest({ url: "/api/role/add", method: "post", data }) } /** * 更新角色 * @param data */ static edit(data: any): Promise { return useRequest({ url: "/api/role/update", method: "post", data }) } /** * 删除角色 * @param userId */ static deleteById(userId: number | string): Promise { return useRequest({ url: `/api/role/delete/${userId}`, method: "post" }) } /** * 批量删除角色 * @param data */ static deleteByIds(data: Array): Promise { return useRequest({ url: "/api/role/delete", method: "post", data }) } /** * 更新角色状态 * @param data */ static updateStatus(data: any): Promise { return useRequest({ url: "/api/role/updateStatus", method: "post", data }) } } ================================================ FILE: ginyi-vue3/src/api/controller/userController.ts ================================================ import useRequest from "@/api/useRequest"; import {IPage} from "@/interface/modules/system"; export class userController { /** * 用户登录 * @param data */ static login(data: any): Promise { return useRequest({ url: "/api/user/login", method: "post", data }) } /** * 退出登录 * @param data */ static logout(): Promise { return useRequest({ url: "/api/user/logout", method: "post", }) } /** * 获取验证码 */ static captcha(): Promise { return useRequest({ url: "/api/verify/captcha", method: "get" }) } /** * 用户列表 */ static getUserList(data: any, pagination?: IPage): Promise { return useRequest({ url: "/api/user/list", method: "post", params: pagination, data }) } /** * 获取用户详情 * @param userId */ static getUserById(userId: number | string): Promise { return useRequest({ url: `/api/user/getUserByUserId/${userId}`, method: "get" }) } /** * 新增用户 * @param data */ static add(data: any): Promise { return useRequest({ url: "/api/user/add", method: "post", data }) } /** * 更新用户 * @param data */ static edit(data: any): Promise { return useRequest({ url: "/api/user/update", method: "post", data }) } /** * 更新用户状态 * @param data */ static updateStatus(data: any): Promise { return useRequest({ url: "/api/user/updateStatus", method: "post", data }) } /** * 删除用户 * @param userId */ static deleteById(userId: number | string): Promise { return useRequest({ url: `/api/user/delete/${userId}`, method: "post" }) } /** * 批量删除用户 * @param data */ static deleteByIds(data: Array): Promise { return useRequest({ url: "/api/user/delete", method: "post", data }) } } ================================================ FILE: ginyi-vue3/src/api/index.ts ================================================ import {userController} from "@/api/controller/userController"; import {menuController} from "@/api/controller/menuController"; import {deptController} from "@/api/controller/deptController"; import {postController} from "@/api/controller/postController"; import {roleController} from "@/api/controller/roleController"; import {logController} from "@/api/controller/logController"; import {onlineController} from "@/api/controller/onlineController"; import {monitorController} from "@/api/controller/monitorController"; export { userController, menuController, deptController, postController, roleController, logController, onlineController, monitorController } ================================================ FILE: ginyi-vue3/src/api/useRequest.tsx ================================================ import axios, {AxiosResponse, InternalAxiosRequestConfig} from "axios"; import {useUserStore} from "@/store/modules/useUserStore"; import {useFormatTime} from "@/hooks/useFormat"; import {storeToRefs} from "pinia"; import {store} from "@/store"; import {setting} from "@/config/setting"; import {useCommonRouter} from "@/router"; import {storage} from "@/hooks/useStorage"; import {useSystemStore} from "@/store/modules/useSystemStore"; const {devBaseURL, prodBaseURL} = setting /** * 创建axios示例 */ const service = axios.create({ baseURL: import.meta.env.DEV ? devBaseURL : prodBaseURL, timeout: 5000, }); /** * 请求拦截 */ service.interceptors.request.use((config: InternalAxiosRequestConfig) => { const {tokenKey, authorization} = storeToRefs(useUserStore(store)); if (authorization && config && config.headers) { typeof config.headers.set === 'function' && config.headers.set(tokenKey?.value, authorization.value) } return config }, (error: any) => { window.$notification.error({ title: "网络请求错误", description: `From ${import.meta.env.DEV ? devBaseURL : prodBaseURL}`, meta: useFormatTime(new Date().valueOf()), content: () => ( {typeof error.message === "string" ? error.message : JSON.stringify(error.message)} ), duration: 5000, keepAliveOnHover: true }) }) /** * 响应拦截 */ service.interceptors.response.use( (response: AxiosResponse) => { const res = response.data; if (res.code !== 200) { switch (res.code) { case 5005: useUserStore(store).$reset() useSystemStore(store).removeAllTabs() useSystemStore(store).resetBreadMenuList() storage.clear() useCommonRouter("login") window.$message.warning(res.msg) break; default: window.$notification.error({ title: res.msg, description: `From ${import.meta.env.DEV ? devBaseURL : prodBaseURL}`, meta: useFormatTime(new Date().valueOf()), content: () => ( {typeof res.data === "string" ? res.data : JSON.stringify(res.data)} ), duration: 5000, keepAliveOnHover: true }) } return Promise.reject(res); } else { return res; } }, (error: any) => { /* 响应拦截失败的情况 */ window.$notification.error({ title: "请求响应错误", description: `From ${import.meta.env.DEV ? devBaseURL : prodBaseURL}`, meta: useFormatTime(new Date().valueOf()), content: () => ( {typeof error.message === "string" ? error.message : JSON.stringify(error.message)} ), duration: 5000, keepAliveOnHover: true }) return Promise.reject(error); } ); export default service ================================================ FILE: ginyi-vue3/src/components/commonForm/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/components/commonForm/props.ts ================================================ export const definedProps = { /** * 确认按钮的文本内容 */ submitButtonText: { type: String, default: "提交" }, /** * 取消按钮的文本内容 */ cancelButtonText: { type: String, default: "重置" }, } ================================================ FILE: ginyi-vue3/src/components/commonForm/useCommonForm.ts ================================================ import {ref} from "vue"; enum actionEnum { ADD = "新增", EDIT = "编辑", QUERY = "查询" } export const useCommonForm = (formDefaultValue: any = {}, addApi?: Function, editApi?: Function,) => { const formRef = ref(undefined) const formActionType = ref(undefined) const formValue = ref({...formDefaultValue}) /** * 新增 */ const onAdd = () => { formActionType.value = actionEnum.ADD formValue.value = {...formDefaultValue} } /** * 编辑 * @param row */ const onEdit = (row: any) => { formActionType.value = actionEnum.EDIT formValue.value = {...row} } /** * 查询 */ const onQuery = () => { return new Promise((resolve, reject) => { formActionType.value = actionEnum.QUERY resolve(formValue.value) }) } /** * 保存 * 返回 Promise,解决函数执行完后需要续写逻辑的需求 */ const onSubmit = () => { return new Promise((resolve, reject) => { // @ts-ignore formRef.value?.validate(err => { if (!err) { } else { } }) }) } /** * 重置 * 返回 Promise,解决函数执行完后需要续写逻辑的需求 */ const onReset = () => { return new Promise((resolve, reject) => { const formDefaultKeys = Object.keys(formDefaultValue) const formKeys = Object.keys(formValue.value) formKeys.map(key => { if (formDefaultKeys.includes(key)) { formValue.value[key] = formDefaultValue[key] } else { formValue.value[key] = null } }) resolve(formValue.value) }) } return { onAdd, onEdit, onQuery, onSubmit, onReset, formValue, formRef } } ================================================ FILE: ginyi-vue3/src/components/commonModal/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/components/commonModal/props.ts ================================================ export const definedProps = { /** * 模型标题 */ title: { type: String, default: "标题" }, /** * 模型预设 */ preset: { type: String, default: "dialog" }, /** * 确定按钮文本内容 */ positiveText: { type: String, default: "确认" }, /** * 取消按钮文本内容 */ negativeText: { type: String, default: "取消" }, show: { type: Boolean, default: false } } ================================================ FILE: ginyi-vue3/src/components/commonModal/useCommonModal.ts ================================================ import {computed, ref} from "vue"; enum actionEnum { ADD = "新增", EDIT = "编辑", } export const useCommonModal = (name: string, formDefaultValue: any = {}, addApi: Function, editApi: Function, deleteApi?: Function, batchDeleteApi?: Function, getDetailsById?: Function) => { const modalShow = ref(false) const modalLoading = ref(false) const modalTitle = ref(undefined) const modalFormRef = ref(undefined) const modalForm = ref({...formDefaultValue}) /** * 新增 */ const onAdd = () => { modalShow.value = true modalLoading.value = false modalTitle.value = actionEnum.ADD modalForm.value = {...formDefaultValue} } /** * 编辑 * @param row * @param id */ const onEdit = (row: any, id: number | string) => { modalShow.value = true modalLoading.value = false modalTitle.value = actionEnum.EDIT if(getDetailsById && id){ getDetailsById(id).then((res: any) => { modalForm.value = {...res.data} }) }else { modalForm.value = {...row} } } /** * 保存 * 返回 Promise,解决函数执行完后需要续写逻辑的需求 */ const onSubmit = (): Promise => { return new Promise((resolve, reject) => { // @ts-ignore modalFormRef.value?.validate(err => { if (!err) { if (modalTitle.value === actionEnum.ADD) { addApi(modalForm.value).then((res: any) => { window.$message.success(res.msg) modalLoading.value = true resolve(null) }) } if (modalTitle.value === actionEnum.EDIT) { editApi(modalForm.value).then((res: any) => { window.$message.success(res.msg) modalLoading.value = true resolve(null) }) } } else { modalShow.value = true } }) }) } /** * 删除 * 返回 Promise,解决函数执行完后需要续写逻辑的需求 */ const onDeleteById = (id: number | string) => { return new Promise((resolve, reject) => { modalLoading.value = false window.$dialog.error({ title: "温馨提醒", content: "删除操作不可逆,是否继续?", positiveText: "确定", negativeText: "取消", onPositiveClick: () => { deleteApi && deleteApi(id).then((res: any) => { window.$message.success(res.msg) modalLoading.value = true resolve(null) }) } }) }) } /** * 批量删除 * 返回 Promise,解决函数执行完后需要续写逻辑的需求 */ const onDeleteByIds = (ids: Array) => { return new Promise((resolve, reject) => { modalLoading.value = false window.$dialog.error({ title: "温馨提醒", content: "删除操作不可逆,是否继续?", positiveText: "确定", negativeText: "取消", onPositiveClick: () => { batchDeleteApi && batchDeleteApi(ids).then((res: any) => { window.$message.success(res.msg) modalLoading.value = true resolve(null) }) } }) }) } return { onAdd, onEdit, onSubmit, onDeleteById, onDeleteByIds, modalForm, modalFormRef, modalShow, modalLoading, modalTitle: computed(() => { return modalTitle.value + name }), } } ================================================ FILE: ginyi-vue3/src/components/commonTable/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/components/commonTable/props.ts ================================================ import {tableActionEnums} from "@/enums/tableActionEnums"; export const definedProps = { /** * 表格的列 */ columns: { require: true, type: Array }, /** * 表格的数据 */ dataList: { type: Array }, /** * 整个组件的尺寸 */ size: { type: String, default: "small" }, /** * 表格的列显示中文的字段,如 {label: 年龄, value: age},则传 label */ labelField: { type: String }, /** * 每一行数据都要有唯一的 key */ rowKey: { require: true, type: Function }, /** * 操作列宽度 */ actionWidth: { type: Number, default: 250 }, /** * 操作列的数据 */ actionColData: { default: () => [], type: Array }, /** * 分页参数对象 */ pagination: { type: [Object, Boolean], default: () => false, }, /** * 是否启用loading加载 */ loading: { type: Boolean }, /** * 横屏滚动区域,启用固定列时必须设置值 */ scrollX: { type: Number, default: 2000 }, /** * 是否显示操作列 */ showActionCol: { type: Boolean, default: true }, /** * 按钮配置 */ buttonConfig: { type: Object, default: () => { return { addButton: { type: "add", title: "新增数据", actionType: tableActionEnums.ADD, show: true, colorType: "primary", }, editButton: { type: "edit", title: "编辑", actionType: tableActionEnums.EDIT, show: true, colorType: "primary", }, deleteButton: { type: "delete", title: "删除", actionType: tableActionEnums.DELETE, show: true, colorType: "error", }, batchDeleteButton: { type: "batchDelete", colorType: "error", actionType: tableActionEnums.BATCH_DELETE, show: true, title: "批量删除", } } } } } ================================================ FILE: ginyi-vue3/src/components/commonTable/useCommonTable.ts ================================================ import {reactive, ref} from "vue"; import {PaginationInfo, PaginationSizeOption} from "naive-ui"; export const useCommonTable = (getListApi: Function) => { const tableTotal = ref(0) const tableDataList = ref>([]) const tableLoading = ref(true) const tablePagination = reactive({ page: 1, pageSize: 10, pageSizes: [10, 30, 50, 100] as Array, itemCount: 0, size: "small", showQuickJumper: true, showSizePicker: true, prefix(info: PaginationInfo) { return `总共 ${info.itemCount} 条` } }) const onPageChange = (page: number) => { tablePagination.page = page getDataList() } const onPageSizeChange = (pageSize: number) => { tablePagination.pageSize = pageSize getDataList() } /** * 获取列表数据 * @param data */ const getDataList = (data: any = {}) => { tableLoading.value = true getListApi(data, { page: tablePagination.page, pageSize: tablePagination.pageSize }).then((res: any) => { tableDataList.value = res.data.list tablePagination.itemCount = res.data.count tableLoading.value = false }).catch((e: any) => { tableLoading.value = true }) } return { tableTotal, tableDataList, tablePagination, tableLoading, onPageChange, onPageSizeChange, getDataList } } ================================================ FILE: ginyi-vue3/src/components/monacoEditor/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/components/monacoEditor/monacoEditorType.ts ================================================ import { PropType } from 'vue' export type Theme = 'vs' | 'hc-black' | 'vs-dark' export type FoldingStrategy = 'auto' | 'indentation' export type RenderLineHighlight = 'all' | 'line' | 'none' | 'gutter' export interface Options { automaticLayout: boolean // 自适应布局 foldingStrategy: FoldingStrategy // 折叠方式 auto | indentation renderLineHighlight: RenderLineHighlight // 行亮 selectOnLineNumbers: boolean // 显示行号 minimap: { // 关闭小地图 enabled: boolean } readOnly: boolean // 只读 fontSize: number // 字体大小 scrollBeyondLastLine: boolean // 取消代码后面一大段空白 overviewRulerBorder: boolean // 不要滚动条的边框 } export const editorProps = { modelValue: { type: String as PropType, default: null, }, width: { type: [String, Number] as PropType, default: '100%', }, height: { type: [String, Number] as PropType, default: '100%', }, language: { type: String as PropType, default: 'javascript', }, theme: { type: String as PropType, validator(value: string): boolean { return ['vs', 'hc-black', 'vs-dark'].includes(value) }, default: 'vs-dark', }, options: { type: Object as PropType, default: function () { return { automaticLayout: true, foldingStrategy: 'indentation', renderLineHighlight: 'all', selectOnLineNumbers: true, minimap: { enabled: true, }, readOnly: false, fontSize: 16, scrollBeyondLastLine: false, overviewRulerBorder: false, } }, }, } ================================================ FILE: ginyi-vue3/src/config/console.log.ts ================================================ export const banner: string = "%c" + " _______ __ __ __ ____ ____ __ \n" + " / _____|| | | \\ | | \\ \\ / / | | \n" + "| | __ | | | \\| | \\ \\/ / | | \n" + "| | |_ | | | | | \\_ _/ | | Version: %c1.0.0%c\n" + "| |__| | | | | |\\ | | | | | 如果你喜欢这个项目,欢迎Star!https://gitee.com/Ginyi/ginyi-spring-vue 💖💖💖\n" + " \\______| |__| |__| \\__| |__| |__| Copyright © 2023-Now Ginyi@aliyun.com. All rights reserved.\n"; console.log(banner, "color:#00a3a3","color:#fdbe24", "color:#00a3a3") ================================================ FILE: ginyi-vue3/src/config/eventBus.ts ================================================ /** * 事件总线 */ import mitt from 'mitt'; export const eventBus = mitt(); ================================================ FILE: ginyi-vue3/src/config/setting.ts ================================================ import {IProject} from "@/interface/modules/system"; import Logo from "@/assets/img/logo.jpg"; /** * 项目配置 */ export const setting: IProject = { devBaseURL: "http://127.0.0.1:8066", prodBaseURL: "http://192.168.0.102:8066", logo: Logo, title: "Ginyi", name: "管理系统", author: "Ginyi@aliyun.com", } ================================================ FILE: ginyi-vue3/src/dictionary/useDynamicDict.ts ================================================ import {deptController, menuController, postController, roleController} from "@/api"; import {useRemoveEmptyChildrenField} from "@/hooks/useTree"; import {ref} from "vue"; export const useDynamicDict = () => { // 部门字典 const deptDict = ref>([]); // 岗位字典 const postDict = ref>([]); // 角色字典 const roleDict = ref>([]); // 菜单字典 const menuDict = ref>([]); /** * 获取部门字典 */ (async () => { if (deptDict.value.length === 0) { const {data} = await deptController.list({}) deptDict.value = useRemoveEmptyChildrenField(data.list) } })(); /** * 获取角色字典 */ (async () => { if (roleDict.value.length === 0) { const {data} = await roleController.list({}) roleDict.value = data.list } })(); /** * 获取岗位字典 */ (async () => { if (postDict.value.length === 0) { const {data} = await postController.list({}) postDict.value = data.list } })(); /** * 获取菜单字典 */ (async () => { if (menuDict.value.length === 0) { const {data} = await menuController.list({filterButton: "0"}) menuDict.value = useRemoveEmptyChildrenField(data.list) } })(); return { deptDict, postDict, roleDict, menuDict } } ================================================ FILE: ginyi-vue3/src/dictionary/useStaticDict.ts ================================================ import {ref} from "vue"; export const useStaticDict = () => { return { // 状态字典 statusDict: ref>([ { label: "正常", value: "0", }, { label: "禁用", value: "1" } ]), // 成功与否字典 successDict: ref>([ { label: "成功", value: "0", }, { label: "失败", value: "1" } ]), // 性别字典 sexDict: ref>([ { label: "男", value: "0", }, { label: "女", value: "1" }, { label: "未知", value: "2" } ]), // 菜单类型字典 menuTypeDict: ref>([ { label: "目录", value: "M", }, { label: "菜单", value: "C" }, { label: "按钮", value: "F" } ]), // 显示隐藏字典 showDict: ref>([ { label: "显示", value: "0", }, { label: "隐藏", value: "1" } ]), // 是否字典 whetherDict: ref>([ { label: "是", value: "0", }, { label: "否", value: "1" } ]), // 操作类型 operationType: ref>([ { label: "其他", value: "0", }, { label: "新增", value: "1", }, { label: "修改", value: "2", }, { label: "删除", value: "3", }, { label: "清除", value: "9", } ]) } } ================================================ FILE: ginyi-vue3/src/directives/draggable.ts ================================================ import {Directive} from "vue"; export const drag: Directive = { mounted(el: HTMLElement) { // 设置目标元素基础属性 el.style.cursor = "move"; el.style.position = "fixed"; el.style.zIndex = "99999"; // 监听鼠标在目标元素上按下 el.addEventListener("mousedown", (e) => { // 当前目标元素的left与top const left = el.offsetLeft; const top = el.offsetTop; // 保存按下的鼠标的X与Y const mouseX = e.clientX; const mouseY = e.clientY; // 监听鼠标移动 document.onmousemove = (e) => { // 鼠标移动的距离 let disX = e.clientX - mouseX; let disY = e.clientY - mouseY; el.style.left = (left + disX) + "px"; el.style.top = (top + disY) + "px"; return false // 防止选中文本,文本拖动的问题 } // 监听鼠标抬起 document.onmouseup = () => { document.onmousemove = null; document.onmouseup = null; } }); } } ================================================ FILE: ginyi-vue3/src/directives/focus.ts ================================================ import {Directive, DirectiveBinding} from "vue"; /** * 自动获取输入框焦点 * 同个页面使用多个v-focus,最后一个生效 */ export const focus: Directive = { mounted(el: HTMLElement, binding: DirectiveBinding) { /** * 原生输入框 v-focus.native */ if (binding.modifiers?.native) { el.focus() return; } /** * n-input v-focus */ const input = el?.childNodes[0]?.childNodes[1]?.childNodes[0]; // @ts-ignore input.focus(); } } ================================================ FILE: ginyi-vue3/src/directives/index.ts ================================================ import {App} from 'vue'; import {focus} from "@/directives/focus"; import {loading} from "@/directives/loading"; import {drag} from "@/directives/draggable" /** * 注册全局自定义指令 * 注意:需要在web-types.json声明一下,避免编译器警告 * @param app */ export const initDirectives = (app: App) => { // 自动获取焦点 app.directive('focus', focus); app.directive('loading', loading); app.directive('drag', drag); } ================================================ FILE: ginyi-vue3/src/directives/loading.ts ================================================ import {createApp, Directive} from "vue"; import NSpin from "naive-ui" /** * 加载中... */ export const loading: Directive = { } ================================================ FILE: ginyi-vue3/src/enums/storeKeyEnums.ts ================================================ export const enum storeKeyEnums { SYSTEM = "system", USER = "user", ROUTER = "router", } ================================================ FILE: ginyi-vue3/src/enums/tableActionEnums.ts ================================================ export enum tableActionEnums { // 新增 ADD = "add", // 编辑 EDIT = "edit", // 删除 DELETE = "delete", // 批量删除 BATCH_DELETE = "BATCH_DELETE", } ================================================ FILE: ginyi-vue3/src/hooks/useColor.ts ================================================ /** * 将传递的百分比与十六进制颜色的R、G或B相加 * @param {string} color 要更改的颜色 * @param {number} amount 更改颜色的量 * @returns {string} 颜色的处理部分 */ function useAddLight(color: string, amount: number): string { const cc = parseInt(color, 16) + amount; const c = cc > 255 ? 255 : cc; return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`; } /** * 根据传递的百分比点亮6个字符的HEX颜色 * @param {string} color 要更改的颜色 * @param {number} amount 更改颜色的量 * @returns {string} 处理后的颜色表示为HEX */ export function useLighten(color: string, amount: number): string { color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color; amount = Math.trunc((255 * amount) / 100); return `#${useAddLight(color.substring(0, 2), amount)}${useAddLight( color.substring(2, 4), amount )}${useAddLight(color.substring(4, 6), amount)}`; } /** * hex转rgba * @param bgColor * @param alpha */ export const useHexToRgba = (bgColor: string, alpha = 0.2): string => { let color = bgColor.slice(1); // 去掉'#'号 let rgba = [ parseInt("0x" + color.slice(0, 2)), parseInt("0x" + color.slice(2, 4)), parseInt("0x" + color.slice(4, 6)), alpha ]; return `rgba(${rgba.toString()})`; }; ================================================ FILE: ginyi-vue3/src/hooks/useCommonColums.ts ================================================ import {useStaticDict} from "@/dictionary/useStaticDict"; export const useCommonColumns = () => { const {statusDict, successDict} = useStaticDict() /** * 渲染状态字典 * @param stateId */ const useRenderStateById = (stateId: string) => { const temp = statusDict.value.filter(state => { return state.value === stateId }) return temp.length !== 0 ? temp[0].label : undefined; } /** * 渲染成功字典 * @param successId */ const useRenderSuccessById = (successId: string) => { const temp = successDict.value.filter(state => { return state.value === successId.toString() }) return temp.length !== 0 ? temp[0].label : undefined; } return { useRenderStateById, useRenderSuccessById } } ================================================ FILE: ginyi-vue3/src/hooks/useDebthro.ts ================================================ /** * 节流 * @param {*} fn 执行函数 * @param {*} wait 节流时间,毫秒 */ export const useThrottle = function (fn: Function, wait?: number) { let timer: any; return function (this: any) { if (!timer) { timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, wait) } } } /** * 防抖 * @param {*} fn 执行函数 * @param {*} wait 防抖时间,毫秒 */ export const useDebounce = function (fn: Function, wait: number) { let timer: any return function (this: any) { // 如果多次触发将上次记录延迟清除掉 if (timer !== null) { clearTimeout(timer) } else { timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, wait) } } } ================================================ FILE: ginyi-vue3/src/hooks/useFormat.ts ================================================ export const useFormatTime = (times: number, showTime = true) => { try { const time = new Date(times) const year = time.getFullYear(); const month = time.getMonth() + 1; const date = time.getDate(); const hour = time.getHours(); const minute = time.getMinutes(); const second = time.getSeconds(); const NewDate = year + "-" + (month < 10 ? '0' + month : month) + "-" + (date < 10 ? '0' + date : date) const NewTime = hour + ":" + (minute < 10 ? '0' + minute : minute) + ":" + (second < 10 ? '0' + second : second); if (showTime) return `${NewDate} ${NewTime}` return NewDate } catch (e) { console.error('时间戳不合法') } } ================================================ FILE: ginyi-vue3/src/hooks/useMenu.ts ================================================ import {useLoadIcon, useRenderIcon} from "@/plugins/naive-ui/common"; /** * 重新格式菜单列表,主要是为了渲染 icon 和 删除空children * @param menuList * @param formatIcon 是否需要处理icon */ export const useMenuFormat = (menuList: any, formatIcon?: boolean): any => { return menuList.map((menu: any) => { if (formatIcon) { menu.icon = useRenderIcon(useLoadIcon(menu.icon)) } if (menu.children?.length > 0) { useMenuFormat(menu.children, formatIcon) } else { delete menu.children } return menu }) } ================================================ FILE: ginyi-vue3/src/hooks/useObject.ts ================================================ import * as Icons from "@vicons/ionicons5"; /** * 简单实现对象的深度克隆 * 注意: * 1、使用该方式会出现一些问题,如值为undefined、函数、Date类型等属性会无法被克隆,此处待完善!!! * 2、非得使用undefined的话,可以使用null替代 * @param obj */ export function useDeepClone(obj: T): T { // 如果是基本数据类型,直接返回 if (typeof obj !== 'object' || obj === null) { return obj; } // 根据类型创建新对象或数组 const newObj = Array.isArray(obj) ? [] : {}; // 递归复制属性或元素 for (const key in obj) { // @ts-ignore newObj[key] = useDeepClone(obj[key]); } return newObj as T; } ================================================ FILE: ginyi-vue3/src/hooks/usePagination.ts ================================================ import {reactive} from "vue"; import {PaginationInfo, PaginationSizeOption} from "naive-ui"; export const usePagination = () => { const pagination = reactive({ page: 1, pageSize: 5, pageSizes: [5, 10, 20, 50] as Array, itemCount: 0, size: "small", showQuickJumper: true, showSizePicker: true, prefix(info: PaginationInfo) { return `总共 ${info.itemCount} 条` } }) const onPageChange = (page: number) => { pagination.page = page } const onPageSizeChange = (pageSize: number) => { pagination.pageSize = pageSize } return { pagination } } ================================================ FILE: ginyi-vue3/src/hooks/usePending.ts ================================================ import axios, {AxiosRequestConfig} from "axios"; const pendingMap = new Map(); /** * 生成每个请求唯一的键 * @param {*} config * @returns string */ export const getPendingKey = (config: AxiosRequestConfig) => { let {url, method, params, data} = config; // response里面返回的config.data是个字符串对象 if (typeof data === 'string') { data = JSON.parse(data); } return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&'); } /** * 储存每个请求唯一值, 也就是cancel()方法, 用于取消请求 * @param {*} config */ export const addPending = (config: AxiosRequestConfig) => { const pendingKey = getPendingKey(config); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { if (!pendingMap.has(pendingKey)) { pendingMap.set(pendingKey, cancel); } }); } /** * 删除重复的请求 * @param {*} config */ export const removePending = (config: AxiosRequestConfig): boolean => { const pendingKey = getPendingKey(config); if (pendingMap.has(pendingKey)) { const cancelToken = pendingMap.get(pendingKey); cancelToken(pendingKey); pendingMap.delete(pendingKey); return true; }else { return false; } } ================================================ FILE: ginyi-vue3/src/hooks/useStorage.ts ================================================ import {storeKeyEnums} from "@/enums/storeKeyEnums"; export class storage { /** * 默认的存储时间(天) */ private static expireTime: number = 7 /** * 设置缓存数据 * @param key * @param value * @param expire 过期时间,单位天 */ static set(key: string, value: any, expire?: number) { const data = JSON.stringify({ value, expire: expire !== undefined ? Date.now() + expire * 60 * 60 * 24 * 7 * 1000 : Date.now() + this.expireTime * 60 * 60 * 24 * 7 * 1000, }); localStorage.setItem(key, data) } /** * 获取缓存数据 * @param key */ static get(key: string): any { const data: any = localStorage.getItem(key) if (data) { const {value, expire} = JSON.parse(data); // 在有效期内直接返回 if ([null, undefined].includes(expire) || expire >= Date.now()) { return value; } // 清除缓存 this.remove(key) return null } } /** * 清除缓存 * @param key */ static remove(key: string) { localStorage.removeItem(key) } /** * 清除缓存,除系统配置外 */ static clear() { let keys: Array = []; for (let i = 0; i < localStorage.length; i++) { keys.push(localStorage.key(i) as string) } keys.map(key => { if (key.indexOf(storeKeyEnums.SYSTEM) === -1) { this.remove(key as string) } }) } } ================================================ FILE: ginyi-vue3/src/hooks/useTree.ts ================================================ /** * * @param name 子节点 name * @param data 要查找的 list * @param result 结果集 * @returns {boolean|*[]} */ export const useFindParentName = (name: string, data: Array = [], result: Array = []): Array | undefined => { for (let i = 0; i < data.length; i++) { const item = data[i]; if (item.name === name) { return result; } if (item.children?.length > 0) { result.push(item.name) const temp = useFindParentName(name, item.children, result); if (temp !== undefined) { return result; } result = [] } } } /** * 根据 name 查找匹配的 node * * @param name * @param data */ export const useFindNodeByName = (name: string, data: Array): any => { for (let i = 0; i < data.length; i++) { const item = data[i]; if (item.name === name) { return item; } else { if (item.children?.length > 0) { const temp = useFindNodeByName(name, item.children); if (temp) { return temp; } } } } } /** * 根据子节点指定的key和对应的value,向上查找其父级 * @param key 如 userId: 123 中的 userId * @param value 如 userId: 123 中的 123 * @param childrenField * @param data * @param result */ export const useFindParentNodes = (key: string, value: string | number, childrenField: string = "children", data: Array = [], result: Array = []): Array | undefined => { for (let i = 0; i < data.length; i++) { const item = data[i]; result.push(item); if (item[key] === value) { return result; } if (item.children?.length > 0) { const temp = useFindParentNodes(key, value, childrenField, item.children, result); if (temp !== undefined) { // 表示找到了 name 匹配的, 替换 result 中的最后一项 (最后一项保存的是同级别下的上一项) if (temp.length === 1) { result[result.length - 1] = temp[0]; } return result; } } result.pop() } } /** * 格式化树 */ export const useRemoveEmptyChildrenField = (data: Array, childrenField: string = "children"): Array => { return data.map((item: any) => { if (item[childrenField]?.length > 0) { useRemoveEmptyChildrenField(item[childrenField], childrenField) } else { delete item[childrenField] } return item }) } /** * 数组扁平化 * @param data * @param result * @param childrenField */ export const useTreeToArray = (data: Array, result: Array = [], childrenField: string = "children"): Array => { data.map(item => { result.push(item) if (item[childrenField]?.length > 0) { useTreeToArray(item[childrenField], result) } }) return result } /** * 搭配 useFindParentNodes 使用(先查找出所有的父级节点) * 数组分割显示 如 总公司 / 市场部门 / 销售岗 */ export const useArraySeparator = (data: Array, key: string, separator: string = "/") => { let result: string = ""; data.forEach((item, index) => { if (index === data.length - 1) { result = `${result} ${item[key]}` } else { result = `${result} ${item[key]} ${separator}` } }) return result; } /** * 克隆出一个对象的属性 * @param data * @param sourceField * @param targetField * @param childrenField */ export const useFieldClone = (data: Array, sourceField: string, targetField: string, childrenField: string = "children") => { const temp = data.map((item: any) => { item[targetField] = item[sourceField] if (item[childrenField]?.length > 0) { useFieldClone(item[childrenField], sourceField, targetField, childrenField) } else { delete item[childrenField] } return item }) return temp } ================================================ FILE: ginyi-vue3/src/interface/modules/system/index.ts ================================================ /** * 项目配置 */ import {Component} from "vue"; export interface IProject { // 开发环境请求地址 devBaseURL: string | undefined; // 生产环境请求地址 prodBaseURL: string | undefined; // 项目logo logo?: string | undefined; // 项目名称 = title + name title?: string | undefined; // 项目名称 = title + name name?: string | undefined; // 作者 author?: string | undefined; } /** * pinia - 系统状态 */ export interface ISystemState { // 系统深色主题 darkTheme?: boolean | undefined; // 系统主题色 themeColor?: string | undefined; // 系统主题色列表 themeColorList?: Array; // 客户端宽度 clientWidth?: number | undefined; // 客户端高度 clientHeight?: number | undefined; // Layout - header 的高度 layoutHeaderHeight?: number | undefined; // Layout - footer 的高度 layoutFooterHeight?: number | undefined; // 侧边栏折叠 collapsed?: boolean | undefined; // logo处的高度 logoHeight?: number | undefined; // 多标签的高度 tabsHeight?: number | undefined; // 多标签页 选中的的索引 tabIndex?: number // 多标签页 列表 tabsList?: Array; // 左侧菜单栏列表 menuList?: Array; // 面包屑导航列表 breadMenuList?: Array; } export interface ITabType { id: number, tabKey: string, tabName: string, icon?: string | Component | Function } /** * pinia - 系统路由 */ export interface IRouterType { // 路由列表 routesList?: Array; // 需要被缓存的路由列表 routesKeepAliveList?: Array } /** * 登录表单 */ export interface ILoginFormType { // 用户名 username: string | undefined; // 密码 password: string | undefined; // 验证码 code: string | undefined; } /** * 注册表单 */ export interface IRegisterFormType { // 用户名 username: string | undefined; // 密码 password: string | number | undefined; // 重复密码 password2: string | number | undefined; // 验证码 code: string | undefined; } /** * 用户 */ export interface IUser { // 用户名 username?: string | undefined; // Token key tokenKey?: string | undefined; // Token令牌 authorization?: string | undefined; } /** * 表格列 */ export interface IColumnType { key: string; title: string; width?: number } /** * 分页 */ export interface IPage { page: number | string; pageSize: number | string } /** * CommonTable按钮配置 */ export interface IButtonConfig { // 按钮类型,如 add 新增 type?: string, // 按钮文本,如 新增 title?: string, // 按钮事件类型 actionType?: any, // 是否显示,必传 show: boolean, // 按钮颜色类型 colorType?: "default" | "tertiary" | "primary" | "success" | "warning" | "error" | "info", } export interface IButtonType { addButton: IButtonConfig, editButton: IButtonConfig, deleteButton: IButtonConfig, batchDeleteButton:IButtonConfig } ================================================ FILE: ginyi-vue3/src/layout/header/breadcrumb.vue ================================================ ================================================ FILE: ginyi-vue3/src/layout/header/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/layout/header/personnel.vue ================================================ ================================================ FILE: ginyi-vue3/src/layout/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/layout/logo/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/layout/menu/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/layout/tabs/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/layout/theme/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/main.ts ================================================ import {createApp} from 'vue' import App from '@/App.vue' import router from "@/router"; import naive from 'naive-ui'; import {initStore} from '@/store'; import "@/plugins/naive-ui/common" import "@/config/console.log"; import {initDirectives} from "@/directives"; import {initIcon} from "@/plugins/naive-ui/common"; import "@/style/index.css" const app = createApp(App) initStore(app) initDirectives(app) initIcon(app) app.use(router) app.use(naive) app.mount('#app') ================================================ FILE: ginyi-vue3/src/plugins/naive-ui/common.tsx ================================================ import type {App} from "vue"; import {Component, computed, createVNode} from "vue"; import {createDiscreteApi, NIcon} from "naive-ui"; import * as Icons from "@vicons/ionicons5"; import {useSystemStore} from "@/store/modules/useSystemStore"; import {storeToRefs} from "pinia"; import {useLighten} from "@/hooks/useColor"; /** * 加延时是为了让 pinia 完成初始化,时间可适当调长些 */ setTimeout(() => { const {getTheme, themeColor} = storeToRefs(useSystemStore()) const configProviderPropsRef = computed(() => ({ theme: getTheme.value, themeOverrides: { common: { primaryColor: themeColor?.value, primaryColorHover: useLighten(themeColor?.value as string, 6), primaryColorPressed: useLighten(themeColor?.value as string, 6), }, LoadingBar: { colorLoading: themeColor?.value as string, }, }, })); const {message, dialog, notification, loadingBar} = createDiscreteApi( ["message", "dialog", "notification", "loadingBar"], { configProviderProps: configProviderPropsRef, } ); window.$message = message; window.$dialog = dialog; window.$notification = notification; window.$loading = loadingBar; }, 100) /** * 加载图标,要配合【 renderIcon 】一起使用 * @param icon */ export const useLoadIcon = (icon: string) => { return Icons[icon as keyof typeof Icons]; } /** * 渲染icon,不一定要配合【 loadIcon 】,可以单独使用 * @param icon */ export const useRenderIcon = (icon: Component) => { return () => { return } } const icon = (props: { icon: string }) => { const {icon} = props; return createVNode(Icons[icon as keyof typeof Icons]); }; export function initIcon(app: App): void { app.component('Icon', icon); } ================================================ FILE: ginyi-vue3/src/plugins/pinia/piniaPlugin.ts ================================================ import {PiniaPluginContext} from "pinia"; import {storage} from "@/hooks/useStorage"; import {toRaw} from "vue"; /** * 做状态持久化 * @param key */ export const piniaPlugin = (key?: string) => { const piniaKey: string = 'PiniaKey' return (context: PiniaPluginContext) => { const {store} = context const data = storage.get(`${key ?? piniaKey}-${store.$id}`) // 每次 pinia 中的状态发生改变时,都会执行 $subscribe store.$subscribe(() => { // store.$state 需要转换成普通对象,可以console.log看看 storage.set(`${key ?? piniaKey}-${store.$id}`, toRaw(store.$state)) }) // 即将 data 解构,后重新赋值给 pinia 的 state,实现持久化 return { ...data } } } ================================================ FILE: ginyi-vue3/src/router/index.ts ================================================ import {createRouter, createWebHashHistory, RouteRecordRaw} from "vue-router"; import {useUserStore} from "@/store/modules/useUserStore"; import {storeToRefs} from "pinia"; import {storage} from "@/hooks/useStorage"; import {store} from "@/store"; import {setting} from "@/config/setting"; import {useRouterStore} from "@/store/modules/useRouterStore"; /** * 系统路由 */ const routes: Array = [ { path: "/", name: "Layout", redirect: "/home", component: () => import("@/layout/index.vue"), children: [ { path: "home", name: "home", meta: {title: "首页"}, component: () => import("@/views/home/index.vue") } ] }, { path: "/login", name: "login", meta: {title: "登录"}, component: () => import("@/views/login/index.vue") } ] const router = createRouter({ history: createWebHashHistory(), routes }) router.beforeEach((to, from, next) => { window.$loading && window.$loading.start(); document.title = `${setting.title} - ${to.meta.title as string}` const {authorization, tokenKey} = storeToRefs(useUserStore(store)); const token = authorization?.value ?? storage.get(tokenKey?.value as string); if (to.name === "login") { if (token) { window.$dialog.warning({ title: "温馨提醒", content: "当前状态【已登录】,是否退出登录回到登录页面?", positiveText: "确定", negativeText: "取消", onPositiveClick: () => { useUserStore(store).logout().then(() => { useRouterStore(store).$reset() next({name: to.name as string}) window.$message.success("退出成功") }) }, onNegativeClick: () => { next({name: from.name as string}) }, onMaskClick: () => { next({name: from.name as string}) }, onClose: () => { next({name: from.name as string}) }, onEsc: () => { next({name: from.name as string}) } }) } else { next() } } else { if (token) { // 注入路由 useRouterStore(store).addRoutes() if (!router.hasRoute(to.name as string)) { next({...to, replace: true}); } else { next(); } } else { next({name: "login"}) } } }) router.afterEach(() => { window.$loading && window.$loading.finish(); }) /** * 封装路由跳转 * @param name 路由name * @param query 路由参数 */ export const useCommonRouter = (name: string, query?: any) => { router.push({ name: name, query: query }) } export default router ================================================ FILE: ginyi-vue3/src/store/index.ts ================================================ import type {App} from 'vue'; import {createPinia} from 'pinia'; import {piniaPlugin} from "@/plugins/pinia/piniaPlugin"; const store = createPinia(); store.use(piniaPlugin()) export const initStore = (app: App) => { app.use(store); } export {store}; ================================================ FILE: ginyi-vue3/src/store/modules/useRouterStore.ts ================================================ import {defineStore} from "pinia"; import {storeKeyEnums} from "@/enums/storeKeyEnums"; import {IRouterType} from "@/interface/modules/system"; import {RouteRecordRaw} from "vue-router"; import router from "@/router"; export const useRouterStore = defineStore(storeKeyEnums.ROUTER, { state: (): IRouterType => ({ routesList: [], routesKeepAliveList: [] }), actions: { setRoutesList(data: Array, prevRoute?: RouteRecordRaw) { data?.forEach((menu: any) => { if (prevRoute) { menu.path = prevRoute ? `${prevRoute.path}/${menu.path}` : menu.path } // C代表菜单,其余是目录和按钮 if (menu.menuType.toUpperCase() === "C") { menu.meta = { title: menu.menuName, keepAlive: menu.isCache === "0" } this.routesList?.push(menu); } if (menu.name && menu.isCache === "0") { this.routesKeepAliveList?.push(menu.name); } if (menu.children?.length > 0) { this.setRoutesList(menu.children, menu) } }) }, addRoutes() { const modules = import.meta.glob('../../views/**/*.vue') this.routesList?.forEach(route => { const temp: RouteRecordRaw = { path: route.path, name: route.name, meta: route.meta, component: modules[`../../views/${route.component}.vue`] } router.addRoute("Layout", temp) }) router.addRoute("Layout", { path: "/:path(.*)", name: "404", component: () => import("@/views/404/index.vue") }) } } }) ================================================ FILE: ginyi-vue3/src/store/modules/useSystemStore.ts ================================================ import {defineStore} from "pinia"; import {storeKeyEnums} from "@/enums/storeKeyEnums"; import {darkTheme} from "naive-ui"; import {BuiltInGlobalTheme} from "naive-ui/es/themes/interface"; import {ISystemState, ITabType} from "@/interface/modules/system"; import {useCommonRouter} from "@/router"; export const useSystemStore = defineStore(storeKeyEnums.SYSTEM, { state: (): ISystemState => ({ darkTheme: false, themeColor: themeColorList[0], themeColorList: themeColorList, clientHeight: document.body.clientHeight, clientWidth: document.body.clientWidth, layoutHeaderHeight: 58, layoutFooterHeight: 40, collapsed: false, logoHeight: 60, tabsHeight: 20, tabIndex: tabsList[0].id, tabsList: tabsList, menuList: [], breadMenuList: [] }), getters: { getTheme(): BuiltInGlobalTheme | undefined { return this.darkTheme ? darkTheme : undefined }, getCollapsed(): boolean | undefined { return this.collapsed }, getMenuList(): any { return this.menuList } }, actions: { setThemeColor(data: string | undefined) { this.themeColor = data }, setClientHeight(data: number | undefined) { this.clientHeight = data }, setClientWidth(data: number | undefined) { this.clientWidth = data }, setCollapsed(data: boolean | undefined) { this.collapsed = data }, setTagIndex(data: number | undefined) { this.tabIndex = data }, addTab(data: ITabType) { const exist = this.tabsList?.some(tab => { return tab.id === data.id }) if (!exist) { this.tabsList?.push(data) } this.tabIndex = data.id setTimeout(() => { const currentTab = document.getElementById(`tabView_${this.tabIndex}`) if (currentTab instanceof HTMLElement) { currentTab.scrollIntoView && currentTab.scrollIntoView({ inline: "center", behavior: "smooth", }); } }, 50) }, removeTab(tabId: number, index: number) { this.tabsList?.splice(index, 1) if (this.tabsList && tabId === this.tabIndex) { this.tabIndex = this.tabsList[0].id useCommonRouter(this.tabsList[0].tabKey) } }, removeLeftTabs(index: number) { this.tabsList?.splice(1, index - 1) const closeSelf = this.tabsList?.some(tab => { return tab.id === this.tabIndex }) if (!closeSelf && this.tabsList) { this.tabIndex = this.tabsList[0].id useCommonRouter(this.tabsList[0].tabKey) } }, removeRightTabs(index: number) { this.tabsList?.splice(index + 1, this.tabsList?.length - 1) const closeSelf = this.tabsList?.some(tab => { return tab.id === this.tabIndex }) if (!closeSelf && this.tabsList) { this.tabIndex = this.tabsList[0].id useCommonRouter(this.tabsList[0].tabKey) } }, removeOtherTabs(index: number) { this.tabsList = this.tabsList?.filter((tab, key) => { return key === 0 || key === index }) const closeSelf = this.tabsList?.some(tab => { return tab.id === this.tabIndex }) if (!closeSelf && this.tabsList) { this.tabIndex = this.tabsList[0].id useCommonRouter(this.tabsList[0].tabKey) } }, removeAllTabs() { this.tabsList = this.tabsList?.filter((tab, key) => { return key === 0 }) if (this.tabsList) { this.tabIndex = this.tabsList[0].id useCommonRouter(this.tabsList[0].tabKey) } }, setMenuList(data: any) { this.menuList = data const hasHome = this.menuList?.some(menu => { return menu.name === "home" }) const has404 = this.menuList?.some(menu => { return menu.name === "404" }) if (!hasHome) { this.menuList?.unshift({ menuId: 0, path: "home", name: "home", icon: "Home", menuName: "首页", component: "home/index", menuType: "C", }) } if (!has404) { this.menuList?.push({ menuId: 199999, path: "404", name: "404", icon: "Bug", show: false, menuName: "404", component: "404/index", menuType: "C", }) } }, setBreadMenuList(data: Array) { this.breadMenuList = [] // 延时主要是为了配合动画效果 setTimeout(() => { this.breadMenuList = data }, 0) }, resetBreadMenuList() { this.breadMenuList = [{ menuId: 0, path: "home", name: "home", icon: "Home", menuName: "首页", component: "home/index", menuType: "C", }] } } }) const themeColorList: Array = [ "#9A53FE", "#e88080", "#0084f4", "#009688", "#536dfe", "#ff5c93", "#ee4f12", "#0096c7", "#9c27b0", "#ff9800", "#FF3D68", "#00C1D4", "#1DB85B", "#2AB193", "#5CC143", "#63e2b7", "#E61E8F", "#E03D3D", "#E63AAF", "#35BA7B", ] const tabsList: Array = [ { id: 0, tabKey: "home", tabName: "首页", icon: "Home" } ] ================================================ FILE: ginyi-vue3/src/store/modules/useUserStore.ts ================================================ import {defineStore} from "pinia"; import {storeKeyEnums} from "@/enums/storeKeyEnums"; import {IUser} from "@/interface/modules/system"; import {storage} from "@/hooks/useStorage"; import {userController} from "@/api"; import {useSystemStore} from "@/store/modules/useSystemStore"; export const useUserStore = defineStore(storeKeyEnums.USER, { state: (): IUser => ({ username: undefined, tokenKey: undefined, authorization: undefined }), getters: { getName(): string | undefined { return this.username }, }, actions: { login(username: string | undefined, data: any): Promise { return new Promise((resolve, reject) => { this.username = username this.tokenKey = data.tokenHeader this.authorization = data.token storage.set(data.tokenHeader, data.token) resolve(null); }) }, logout(): Promise { return new Promise((resolve, reject) => { userController.logout().then(() => { useUserStore().$reset() useSystemStore().removeAllTabs() useSystemStore().resetBreadMenuList() storage.clear() resolve(null); }) }) } } }) ================================================ FILE: ginyi-vue3/src/style/index.css ================================================ /* 全局样式 */ .n-card.n-card--bordered { border-radius: 10px; } /* 滚动条的宽度 */ ::-webkit-scrollbar { width: 6px; } /* 滚动条里面小方块 */ ::-webkit-scrollbar-thumb { border-radius: 4px; box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); background: rgba(153, 153, 153, 0.3); } /* 滚动条里面轨道 */ ::-webkit-scrollbar-track { border-radius: 0; } /* 内容主体区过渡动画 */ .fade-main-move, .fade-main-enter-active, .fade-main-leave-active { transition: all 0.3s ease; } .fade-main-enter-from, .fade-main-leave-to { opacity: 0; transform: translateX(30px); } .fade-main-leave-active { position: absolute; } /* 抽屉面板内容区 */ .n-drawer .n-drawer-content .n-drawer-body-content-wrapper { box-sizing: border-box; padding: 0; } ================================================ FILE: ginyi-vue3/src/views/404/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/home/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/login/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/login/loginForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/login/registerForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/monitor/cache/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/monitor/cacheList/columns.tsx ================================================ import {DataTableColumns, NButton, NSpace} from "naive-ui"; import {eventBus} from "@/config/eventBus"; import {h} from "vue"; /** * 查看详情的点击事件 * @param row */ const handleClickDetails = (row: any) => { eventBus.emit("handleClickDetails", row) } /** * 删除的点击事件 * @param row */ const handleClickDelete = (row: any) => { eventBus.emit("handleClickDelete", row) } export const columns: DataTableColumns = [ { title: "键名", key: "key", ellipsis: { tooltip: true } }, { title: "操作", key: "action", width: 160, render: (row) => ( h(NSpace, null, () => [ h(NButton, { type: "primary", size: "small", onClick: () => handleClickDetails(row)}, {default: () => "查看详情"}), h(NButton, {type: "error", size: "small", onClick: () => handleClickDelete(row)}, {default: () => "删除"}) ]) ) } ] ================================================ FILE: ginyi-vue3/src/views/pages/monitor/cacheList/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/monitor/data/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/monitor/online/columns.tsx ================================================ import {DataTableColumns} from "naive-ui"; export const columns: DataTableColumns = [ { title: "会话编号", key: "token", width: 200, ellipsis: { tooltip: true } }, { title: "登录名称", key: "username" }, { title: "主机IP", key: "ipaddr", }, { title: "登录地点", key: "loginLocation", }, { title: "浏览器", key: "browser", }, { title: "操作系统", key: "os", }, { title: "登录时间", key: "loginTime", width: 200 } ] ================================================ FILE: ginyi-vue3/src/views/pages/monitor/online/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/monitor/service/columns.tsx ================================================ import {DataTableColumns, NTag} from "naive-ui"; export const columns: DataTableColumns = [ { title: "盘符路径", key: "dirName", }, { title: "盘符类型", key: "typeName", }, { title: "总大小", key: "total", render: (row) => ( {row.total}GB ) }, { title: "已用大小", key: "used", render: (row) => ( {row.used}GB ) }, { title: "可用大小", key: "free", render: (row) => ( {row.free}GB ) }, { title: "已用百分比", key: "usage", render: (row) => ( {row.usage}% ) }, ] ================================================ FILE: ginyi-vue3/src/views/pages/monitor/service/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/monitor/task/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/order/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/product/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/department/columns.tsx ================================================ import {DataTableColumns, NSwitch, NTag} from "naive-ui"; import {useCommonColumns} from "@/hooks/useCommonColums"; import {h} from "vue"; import {eventBus} from "@/config/eventBus"; const {useRenderStateById} = useCommonColumns() /** * 状态的点击事件 * @param row */ const handleSwitchClick = (row: any) => { eventBus.emit("handleDeptStatusSwitchClick", row) } export const columns: DataTableColumns = [ { title: "部门名称", key: "deptName" }, { title: "状态", key: "status", render: (row) => ( h(NSwitch, { value: row.status === "0", onClick: () => handleSwitchClick(row) }, { checked: () => "正常", unchecked: () => "禁用" }) ) }, { title: "排序", key: "sort", }, { title: "创建时间", key: "createTime", } ] ================================================ FILE: ginyi-vue3/src/views/pages/system/department/deptEditForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/department/deptQueryForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/department/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/dict/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/log/login/columns.tsx ================================================ import {DataTableColumns, NTag} from "naive-ui"; import {useCommonColumns} from "@/hooks/useCommonColums"; const {useRenderSuccessById} = useCommonColumns() export const columns: DataTableColumns = [ { title: "用户名称", key: "userName", }, { title: "登录地址", key: "ipaddr" }, { title: "登录地点", key: "loginLocation", }, { title: "浏览器", key: "browser", }, { title: "操作系统", key: "os", }, { title: "登录状态", key: "status", render: (row) => ( {useRenderSuccessById(row.status)} ) }, { title: "操作信息", key: "msg", ellipsis: { tooltip: true } }, { title: "登录日期", key: "createTime", width: 200 } ] ================================================ FILE: ginyi-vue3/src/views/pages/system/log/login/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/log/operation/columns.tsx ================================================ import {DataTableColumns, NTag} from "naive-ui"; import {useCommonColumns} from "@/hooks/useCommonColums"; import {useColumns} from "@/views/pages/system/log/operation/useColumns"; const {useRenderSuccessById} = useCommonColumns() const {useRenderOperationType} = useColumns() export const columns: DataTableColumns = [ { title: "操作人员", key: "operationName", }, { title: "操作地址", key: "operationIp" }, { title: "操作地点", key: "operationLocation", }, { title: "系统模块", key: "title", }, { title: "操作类型", key: "businessType", render: (row) => ( {useRenderOperationType(row.businessType.toString())} ) }, { title: "请求方式", key: "requestMethod", }, { title: "操作状态", key: "status", render: (row) => ( {useRenderSuccessById(row.status)} ) }, { title: "操作时间", key: "createTime", width: 200 } ] ================================================ FILE: ginyi-vue3/src/views/pages/system/log/operation/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/log/operation/useColumns.ts ================================================ import {useStaticDict} from "@/dictionary/useStaticDict"; export const useColumns = () => { const {operationType} = useStaticDict() const useRenderOperationType = (operation: string) => { const temp = operationType.value.filter(type => { return type.value === operation }) return temp.length !== 0 ? temp[0].label : undefined; } return { useRenderOperationType } } ================================================ FILE: ginyi-vue3/src/views/pages/system/menu/columns.tsx ================================================ import {DataTableColumns, NSwitch, NTag} from "naive-ui"; import {useColumns} from "@/views/pages/system/menu/useColumns"; import {useCommonColumns} from "@/hooks/useCommonColums"; import {h} from "vue"; import {eventBus} from "@/config/eventBus"; const {useRenderStateById} = useCommonColumns() const {useRenderMenuType} = useColumns() /** * 状态的点击事件 * @param row */ const handleSwitchClick = (row: any) => { eventBus.emit("handleMenuStatusSwitchClick", row) } export const columns: DataTableColumns = [ { title: "菜单名称", key: "menuName", ellipsis: { tooltip: true } }, { title: "图标", key: "icon", ellipsis: { tooltip: true } }, { title: "状态", key: "status", width: 150, render: (row) => ( h(NSwitch, { value: row.status === "0", onClick: () => handleSwitchClick(row) }, { checked: () => "正常", unchecked: () => "禁用" }) ) }, { title: "菜单类型", key: "menuType", render: (row) => ( {useRenderMenuType(row.menuType)} ) }, { title: "路由名称", key: "name", align: "center", render: (row) => ( {[undefined, null, ""].includes(row.name) ? "-" : row.name} ) }, { title: "权限标识", key: "perms", width: 200, ellipsis: { tooltip: true }, align: "center", render: (row) => ( {[undefined, null, ""].includes(row.perms) ? "-" : row.perms} ) }, { title: "组件路径", key: "component", width: 300, render: (row) => ( {[undefined, null, ""].includes(row.component) ? "-" : row.component} ) }, { title: "排序", key: "sort", }, { title: "创建时间", key: "createTime", } ] ================================================ FILE: ginyi-vue3/src/views/pages/system/menu/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/menu/menuEditForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/menu/menuQueryForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/menu/useColumns.ts ================================================ import {useStaticDict} from "@/dictionary/useStaticDict"; export const useColumns = () => { const {menuTypeDict} = useStaticDict() const useRenderMenuType = (menuType: string) => { const temp = menuTypeDict.value.filter(type => { return type.value === menuType }) return temp.length !== 0 ? temp[0].label : undefined; } return { useRenderMenuType } } ================================================ FILE: ginyi-vue3/src/views/pages/system/notice/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/params/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/position/columns.tsx ================================================ import {DataTableColumns, NSwitch, NTag} from "naive-ui"; import {useCommonColumns} from "@/hooks/useCommonColums"; import {h} from "vue"; import {eventBus} from "@/config/eventBus"; const {useRenderStateById} = useCommonColumns() /** * 状态的点击事件 * @param row */ const handleSwitchClick = (row: any) => { eventBus.emit("handlePostStatusSwitchClick", row) } export const columns: DataTableColumns = [ { title: "岗位名称", key: "postName" }, { title: "岗位编码", key: "postCode" }, { title: "状态", key: "status", render: (row) => ( h(NSwitch, { value: row.status === "0", onClick: () => handleSwitchClick(row) }, { checked: () => "正常", unchecked: () => "禁用" }) ) }, { title: "排序", key: "sort", }, { title: "创建时间", key: "createTime", width: 200 } ] ================================================ FILE: ginyi-vue3/src/views/pages/system/position/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/position/postEditForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/position/postQueryForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/role/columns.tsx ================================================ import {DataTableColumns, NSwitch, NTag} from "naive-ui"; import {useCommonColumns} from "@/hooks/useCommonColums"; import {h} from "vue"; import {eventBus} from "@/config/eventBus"; const {useRenderStateById} = useCommonColumns() /** * 状态的点击事件 * @param row */ const handleSwitchClick = (row: any) => { eventBus.emit("handleRoleStatusSwitchClick", row) } export const columns: DataTableColumns = [ { title: "角色名称", key: "roleName" }, { title: "角色权限字符串", key: "roleKey" }, { title: "状态", key: "status", render: (row) => ( h(NSwitch, { value: row.status === "0", onClick: () => handleSwitchClick(row) }, { checked: () => "正常", unchecked: () => "禁用" }) ) }, { title: "排序", key: "sort", width: 80, }, { title: "创建时间", key: "createTime", width: 200 } ] ================================================ FILE: ginyi-vue3/src/views/pages/system/role/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/role/roleEditForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/role/roleQueryForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/user/columns.tsx ================================================ import {DataTableColumns, NImage, NSpace, NSwitch, NTag} from "naive-ui"; import {useColumns} from "@/views/pages/system/user/useColumns"; import {setting} from "@/config/setting"; import {h} from "vue"; import {eventBus} from "@/config/eventBus"; const {logo} = setting const { useRenderSexById, useRenderDeptNameById, useRenderRoleNameByIds, useRenderPostNameByIds } = useColumns() /** * 状态的点击事件 * @param row */ const handleSwitchClick = (row: any) => { eventBus.emit("handleUserStatusSwitchClick", row) } export const columns: DataTableColumns = [ { title: "用户昵称", key: "nickName" }, { title: "用户账号", key: "userName" }, { title: "头像", key: "avatar", width: 80, render: (row) => ( ) }, { title: "性别", key: "sex", width: 80, render: (row) => ( {useRenderSexById(row.sex)} ) }, { title: "状态", key: "status", width: 150, render: (row) => ( h(NSwitch, { value: row.status === "0", onClick: () => handleSwitchClick(row) }, { checked: () => "正常", unchecked: () => "禁用" }) ) }, { title: "部门", key: "deptId", width: 300, ellipsis: { tooltip: true }, render: (row) => ( {useRenderDeptNameById(row.deptId)} ) }, { title: "岗位", key: "postIds", width: 200, render: (row) => ( {useRenderPostNameByIds(row.postIds)?.map(post => {post})} ) }, { title: "角色", key: "roleIds", width: 200, ellipsis: { tooltip: true }, render: (row) => ( {useRenderRoleNameByIds(row.roleIds)?.map(role => {role})} ) }, { title: "手机号码", key: "phoneNumber" }, { title: "创建时间", key: "createTime", width: 200 }, { title: "更新时间", key: "updateTime", width: 200 } ] ================================================ FILE: ginyi-vue3/src/views/pages/system/user/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/user/useColumns.ts ================================================ import {useStaticDict} from "@/dictionary/useStaticDict"; import {useDynamicDict} from "@/dictionary/useDynamicDict"; import {useArraySeparator, useFindParentNodes, useTreeToArray} from "@/hooks/useTree"; export const useColumns = () => { const {statusDict, sexDict} = useStaticDict() const {deptDict, roleDict, postDict} = useDynamicDict() const useRenderSexById = (sexId: string) => { const temp = sexDict.value.filter(sex => { return sex.value === sexId }) return temp.length !== 0 ? temp[0].label : undefined; } const useRenderDeptNameById = (deptId: string | number) => { const dataList = useFindParentNodes("deptId", deptId, "children", deptDict.value) return useArraySeparator(dataList as Array, "deptName") } const useRenderRoleNameByIds = (roleIds: Array) => { const temp = roleDict.value.filter(role => { return roleIds.includes(role.roleId) }) if (temp.length !== 0) { return temp.map(role => role.roleName) } } const useRenderPostNameByIds = (postIds: Array) => { const temp = postDict.value.filter(post => { return postIds.includes(post.postId) }) if (temp.length !== 0) { return temp.map(post => post.postName) } } return { useRenderSexById, useRenderDeptNameById, useRenderRoleNameByIds, useRenderPostNameByIds } } ================================================ FILE: ginyi-vue3/src/views/pages/system/user/userEditForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/system/user/userQueryForm.vue ================================================ ================================================ FILE: ginyi-vue3/src/views/pages/systools/code/index.vue ================================================ ================================================ FILE: ginyi-vue3/src/vite-env.d.ts ================================================ import {MessageApi} from "naive-ui/es/message"; import {NotificationApi} from "naive-ui/es/notification"; import {DialogApi} from "naive-ui/es/dialog"; import {LoadingBarApi} from "naive-ui/es/loading-bar"; declare global { interface Window { $message: MessageApi; $dialog: DialogApi; $notification: NotificationApi; $loading: LoadingBarApi; } declare module '*.svg' declare module '*.png' declare module '*.jpg' declare module '*.jpeg' declare module '*.gif' declare module '*.bmp' declare module '*.tiff' } ================================================ FILE: ginyi-vue3/tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "useDefineForClassFields": true, "module": "ESNext", "moduleResolution": "Node", "strict": true, "jsx": "preserve", "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, "lib": ["ESNext", "DOM"], "skipLibCheck": true, "noEmit": true, "baseUrl": ".", "paths": { "@/*": [ "src/*" ] }, "types": ["vite/client"] }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "references": [ {"path": "./tsconfig.node.json"} ] } ================================================ FILE: ginyi-vue3/tsconfig.node.json ================================================ { "compilerOptions": { "composite": true, "module": "ESNext", "moduleResolution": "Node", "allowSyntheticDefaultImports": true }, "include": ["vite.config.ts"] } ================================================ FILE: ginyi-vue3/vite.config.ts ================================================ import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' import {resolve} from 'path' import vueJsxPlugin from "@vitejs/plugin-vue-jsx"; export default defineConfig({ envDir: ".", plugins: [vue(), vueJsxPlugin()], server: { host: '0.0.0.0', // 主机地址 open: true, // 自动打开浏览器 port: 3800 // 服务端口 }, // 路径别名 resolve: { alias: { '@': resolve(__dirname, './src') } }, optimizeDeps: { include: [ `monaco-editor/esm/vs/language/json/json.worker`, `monaco-editor/esm/vs/language/css/css.worker`, `monaco-editor/esm/vs/language/html/html.worker`, `monaco-editor/esm/vs/language/typescript/ts.worker`, `monaco-editor/esm/vs/editor/editor.worker` ], } }) ================================================ FILE: ginyi-vue3/web-types.json ================================================ { "$schema": "https://json.schemastore.org/web-types", "framework": "vue", "name": "", "version": "", "contributions": { "html": { "js-types-syntax": "typescript", "attributes": [ { "name": "v-focus", "description": "自动获取输入框焦点\n1:naive-ui:v-focus \n2:原生控件:v-focus.native" }, { "name": "v-loading", "description": "加载中\ntrue开启\nfalse关闭" }, { "name": "v-drag", "description": "使元素可被拖拽" } ] } } }