├── .gitignore ├── README.md ├── api ├── .gitignore ├── LICENSE ├── pom.xml └── src │ ├── main │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── guojiaxing1995 │ │ │ └── easyJmeter │ │ │ ├── EasyJmeterApplication.java │ │ │ ├── bo │ │ │ ├── FileBO.java │ │ │ ├── GroupPermissionBO.java │ │ │ └── LoginCaptchaBO.java │ │ │ ├── common │ │ │ ├── LocalUser.java │ │ │ ├── aop │ │ │ │ └── ResultAspect.java │ │ │ ├── configuration │ │ │ │ ├── CacheConfiguration.java │ │ │ │ ├── CodeMessageConfiguration.java │ │ │ │ ├── CommonConfiguration.java │ │ │ │ ├── CustomServletModelAttributeMethodProcessor.java │ │ │ │ ├── CustomServletRequestDataBinder.java │ │ │ │ ├── InfluxDBConfiguration.java │ │ │ │ ├── InfluxDBProperties.java │ │ │ │ ├── LoginCaptchaProperties.java │ │ │ │ ├── MinioConfiguration.java │ │ │ │ ├── SecurityConfiguration.java │ │ │ │ ├── SocketIOClientConfiguration.java │ │ │ │ ├── SocketIOServerConfiguration.java │ │ │ │ ├── SwaggerConfiguration.java │ │ │ │ ├── SwaggerProperties.java │ │ │ │ └── WebConfiguration.java │ │ │ ├── constant │ │ │ │ └── IdentityConstant.java │ │ │ ├── enumeration │ │ │ │ ├── DebugTypeEnum.java │ │ │ │ ├── GroupLevelEnum.java │ │ │ │ ├── JmeterStatusEnum.java │ │ │ │ ├── LogLevelEnum.java │ │ │ │ └── TaskResultEnum.java │ │ │ ├── exception │ │ │ │ └── RestExceptionHandler.java │ │ │ ├── factory │ │ │ │ └── YamlPropertySourceFactory.java │ │ │ ├── interceptor │ │ │ │ ├── AuthorizeVerifyResolverImpl.java │ │ │ │ ├── LoggerImpl.java │ │ │ │ └── RequestLogInterceptor.java │ │ │ ├── jmeter │ │ │ │ ├── InitServerJmeterRunner.java │ │ │ │ ├── JmeterExternal.java │ │ │ │ ├── LinkStrategy.java │ │ │ │ ├── ReportDataProcess.java │ │ │ │ ├── links │ │ │ │ │ ├── CleanLink.java │ │ │ │ │ ├── CollectLink.java │ │ │ │ │ ├── ConfigureLink.java │ │ │ │ │ └── RunLink.java │ │ │ │ └── xstream │ │ │ │ │ ├── AssertionResult.java │ │ │ │ │ ├── HttpSample.java │ │ │ │ │ ├── JmeterLogDeal.java │ │ │ │ │ └── TestResults.java │ │ │ ├── listener │ │ │ │ └── PermissionHandleListener.java │ │ │ ├── mybatis │ │ │ │ └── Page.java │ │ │ ├── serializer │ │ │ │ ├── DeserializerObjectMapper.java │ │ │ │ ├── JmeterStatusEnumDeserializer.java │ │ │ │ ├── JmeterStatusEnumSerializer.java │ │ │ │ ├── TaskResultEnumDeserializer.java │ │ │ │ └── TaskResultEnumSerializer.java │ │ │ ├── task │ │ │ │ ├── AgentScheduleTask.java │ │ │ │ └── ServerScheduleTask.java │ │ │ └── util │ │ │ │ ├── CSVUtil.java │ │ │ │ ├── CaptchaUtil.java │ │ │ │ ├── EnumUtil.java │ │ │ │ ├── IPUtil.java │ │ │ │ ├── PageUtil.java │ │ │ │ ├── ResponseUtil.java │ │ │ │ ├── ThreadUtil.java │ │ │ │ └── ZipUtil.java │ │ │ ├── controller │ │ │ ├── cms │ │ │ │ ├── AdminController.java │ │ │ │ ├── FileController.java │ │ │ │ ├── LogController.java │ │ │ │ └── UserController.java │ │ │ └── v1 │ │ │ │ ├── BookController.java │ │ │ │ ├── CaseController.java │ │ │ │ ├── CommonController.java │ │ │ │ ├── JFileController.java │ │ │ │ ├── MachineController.java │ │ │ │ ├── ProjectController.java │ │ │ │ ├── SocketIOClientHandler.java │ │ │ │ ├── SocketIOServerHandler.java │ │ │ │ └── TaskController.java │ │ │ ├── dto │ │ │ ├── admin │ │ │ │ ├── DispatchPermissionDTO.java │ │ │ │ ├── DispatchPermissionsDTO.java │ │ │ │ ├── NewGroupDTO.java │ │ │ │ ├── QueryUsersDTO.java │ │ │ │ ├── RemovePermissionsDTO.java │ │ │ │ ├── ResetPasswordDTO.java │ │ │ │ ├── UpdateGroupDTO.java │ │ │ │ └── UpdateUserInfoDTO.java │ │ │ ├── book │ │ │ │ └── CreateOrUpdateBookDTO.java │ │ │ ├── file │ │ │ │ └── CreateFileDTO.java │ │ │ ├── jcase │ │ │ │ ├── CaseDebugDTO.java │ │ │ │ └── CreateOrUpdateCaseDTO.java │ │ │ ├── log │ │ │ │ └── QueryLogDTO.java │ │ │ ├── machine │ │ │ │ ├── CreateOrUpdateMachineDTO.java │ │ │ │ └── HeartBeatMachineDTO.java │ │ │ ├── project │ │ │ │ └── CreateOrUpdateProjectDTO.java │ │ │ ├── query │ │ │ │ └── BasePageDTO.java │ │ │ ├── task │ │ │ │ ├── CreateOrUpdateTaskDTO.java │ │ │ │ ├── JmeterAggregateReportDTO.java │ │ │ │ ├── JmeterParamDTO.java │ │ │ │ ├── ModifyTaskDTO.java │ │ │ │ ├── TaskMachineDTO.java │ │ │ │ ├── TaskProgressMachineDTO.java │ │ │ │ ├── TaskRealTimeDTO.java │ │ │ │ └── TaskSearchDTO.java │ │ │ └── user │ │ │ │ ├── ChangePasswordDTO.java │ │ │ │ ├── LoginDTO.java │ │ │ │ ├── RegisterDTO.java │ │ │ │ └── UpdateInfoDTO.java │ │ │ ├── extension │ │ │ └── file │ │ │ │ ├── LocalUploader.java │ │ │ │ ├── QiniuUploader.java │ │ │ │ ├── UploaderConfiguration.java │ │ │ │ └── config.yml │ │ │ ├── mapper │ │ │ ├── BookMapper.java │ │ │ ├── CaseMapper.java │ │ │ ├── FileMapper.java │ │ │ ├── GroupMapper.java │ │ │ ├── GroupPermissionMapper.java │ │ │ ├── JFileMapper.java │ │ │ ├── LogMapper.java │ │ │ ├── MachineMapper.java │ │ │ ├── PermissionMapper.java │ │ │ ├── ProjectMapper.java │ │ │ ├── TaskLogMapper.java │ │ │ ├── TaskMapper.java │ │ │ ├── UserGroupMapper.java │ │ │ ├── UserIdentityMapper.java │ │ │ └── UserMapper.java │ │ │ ├── model │ │ │ ├── AggregateReportDO.java │ │ │ ├── BaseModel.java │ │ │ ├── BookDO.java │ │ │ ├── CaseDO.java │ │ │ ├── FileDO.java │ │ │ ├── GroupDO.java │ │ │ ├── GroupPermissionDO.java │ │ │ ├── JFileDO.java │ │ │ ├── LogDO.java │ │ │ ├── MachineDO.java │ │ │ ├── PermissionDO.java │ │ │ ├── ProjectDO.java │ │ │ ├── ReportDO.java │ │ │ ├── StatisticsDO.java │ │ │ ├── TaskDO.java │ │ │ ├── TaskLogDO.java │ │ │ ├── UserDO.java │ │ │ ├── UserGroupDO.java │ │ │ └── UserIdentityDO.java │ │ │ ├── module │ │ │ ├── file │ │ │ │ ├── AbstractUploader.java │ │ │ │ ├── File.java │ │ │ │ ├── FileConstant.java │ │ │ │ ├── FileProperties.java │ │ │ │ ├── FileUtil.java │ │ │ │ ├── UploadHandler.java │ │ │ │ └── Uploader.java │ │ │ ├── log │ │ │ │ ├── JmeterLogFilter.java │ │ │ │ ├── MDCAccessConstant.java │ │ │ │ └── MDCAccessServletFilter.java │ │ │ └── message │ │ │ │ ├── MessageConstant.java │ │ │ │ ├── MessageWebSocketHandler.java │ │ │ │ ├── WebSocketInterceptor.java │ │ │ │ ├── WebsocketConfiguration.java │ │ │ │ ├── WsHandler.java │ │ │ │ └── WsHandlerImpl.java │ │ │ ├── repository │ │ │ ├── AggregateReportRepository.java │ │ │ ├── ReportRepository.java │ │ │ └── StatisticsRepository.java │ │ │ ├── service │ │ │ ├── AdminService.java │ │ │ ├── BookService.java │ │ │ ├── CaseService.java │ │ │ ├── CommonService.java │ │ │ ├── FileService.java │ │ │ ├── GroupService.java │ │ │ ├── JFileService.java │ │ │ ├── LogService.java │ │ │ ├── MachineService.java │ │ │ ├── PermissionService.java │ │ │ ├── ProjectService.java │ │ │ ├── TaskInfluxdbService.java │ │ │ ├── TaskLogService.java │ │ │ ├── TaskService.java │ │ │ ├── UserIdentityService.java │ │ │ ├── UserService.java │ │ │ └── impl │ │ │ │ ├── AdminServiceImpl.java │ │ │ │ ├── BookServiceImpl.java │ │ │ │ ├── CaseServiceImpl.java │ │ │ │ ├── CommonServiceImpl.java │ │ │ │ ├── FileServiceImpl.java │ │ │ │ ├── GroupServiceImpl.java │ │ │ │ ├── JFileServiceImpl.java │ │ │ │ ├── LogServiceImpl.java │ │ │ │ ├── MachineServiceImpl.java │ │ │ │ ├── PermissionServiceImpl.java │ │ │ │ ├── ProjectServiceImpl.java │ │ │ │ ├── TaskInfluxdbServiceImpl.java │ │ │ │ ├── TaskLogServiceImpl.java │ │ │ │ ├── TaskServiceImpl.java │ │ │ │ ├── UserIdentityServiceImpl.java │ │ │ │ └── UserServiceImpl.java │ │ │ └── vo │ │ │ ├── CaseDebugVO.java │ │ │ ├── CaseInfoPlusVO.java │ │ │ ├── CaseInfoVO.java │ │ │ ├── CreatedVO.java │ │ │ ├── CutFileVO.java │ │ │ ├── DeletedVO.java │ │ │ ├── HistoryTaskVO.java │ │ │ ├── JFileVO.java │ │ │ ├── LoginCaptchaVO.java │ │ │ ├── MachineCutFileVO.java │ │ │ ├── PageResponseVO.java │ │ │ ├── ProjectInfoVO.java │ │ │ ├── TaskInfoVO.java │ │ │ ├── TaskProgressVO.java │ │ │ ├── UnifyResponseVO.java │ │ │ ├── UpdatedVO.java │ │ │ ├── UserInfoVO.java │ │ │ └── UserPermissionVO.java │ └── resources │ │ ├── DejaVuSerif-Bold.ttf │ │ ├── Dockerfile │ │ ├── ValidationMessages.properties │ │ ├── apache-jmeter.zip │ │ ├── application-dev.yml │ │ ├── application-prod.yml │ │ ├── application-test.yml │ │ ├── application.yml │ │ ├── banner.txt │ │ ├── code-message.properties │ │ ├── docker-entrypoint.sh │ │ ├── logback-spring.xml │ │ ├── mapper │ │ ├── BookMapper.xml │ │ ├── CaseMapper.xml │ │ ├── FileMapper.xml │ │ ├── GroupMapper.xml │ │ ├── GroupPermissionMapper.xml │ │ ├── JFileMapper.xml │ │ ├── LogMapper.xml │ │ ├── MachineMapper.xml │ │ ├── PermissionMapper.xml │ │ ├── ProjectMapper.xml │ │ ├── TaskLogMapper.xml │ │ ├── TaskMapper.xml │ │ ├── UserGroupMapper.xml │ │ ├── UserIdentityMapper.xml │ │ └── UserMapper.xml │ │ └── schema.sql │ └── test │ ├── java │ ├── CodeGenerator.java │ ├── Jackson2Test.java │ └── io │ │ └── github │ │ └── guojiaxing1995 │ │ └── easyJmeter │ │ ├── LatticyApplicationTests.java │ │ ├── controller │ │ ├── cms │ │ │ ├── AdminControllerTest.java │ │ │ ├── LogControllerTest.java │ │ │ └── UserControllerTest.java │ │ └── v1 │ │ │ └── BookControllerTest.java │ │ ├── mapper │ │ ├── BookMapperTest.java │ │ ├── FileMapperTest.java │ │ ├── GroupMapperTest.java │ │ ├── LogMapperTest.java │ │ └── UserMapperTest.java │ │ └── service │ │ └── impl │ │ ├── AdminServiceImplTest.java │ │ ├── BookServiceImplTest.java │ │ ├── GroupServiceImplTest.java │ │ ├── LogServiceImplTest.java │ │ ├── PermissionServiceImplTest.java │ │ ├── UserIdentityServiceImplTest.java │ │ └── UserServiceImplTest.java │ └── resources │ ├── h2-test.sql │ └── mpg │ └── templates │ ├── controller.java.ftl │ ├── entity.java.ftl │ ├── mapper.xml.ftl │ └── premium │ └── controller.java.ftl ├── docker-compose.yaml └── web ├── .editorconfig ├── .env.development ├── .env.production ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── .travis.yml ├── Dockerfile ├── LICENSE ├── babel.config.js ├── default.conf ├── jest.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── iconfont.css ├── icons │ └── your-icon.png ├── index.html ├── robots.txt ├── static │ └── img │ │ └── logo.png └── tinymce │ ├── langs │ └── zh_CN.js │ └── skins │ ├── content │ ├── default │ │ ├── content.css │ │ └── content.min.css │ ├── document │ │ ├── content.css │ │ └── content.min.css │ └── writer │ │ ├── content.css │ │ └── content.min.css │ └── ui │ ├── oxide-dark │ ├── content.css │ ├── content.inline.css │ ├── content.inline.min.css │ ├── content.min.css │ ├── skin.css │ └── skin.min.css │ └── oxide │ ├── content.css │ ├── content.inline.css │ ├── content.inline.min.css │ ├── content.min.css │ ├── content.mobile.min.css │ ├── fonts │ └── tinymce-mobile.woff │ ├── skin.css │ ├── skin.min.css │ └── skin.mobile.min.css ├── script ├── lib │ ├── exec-promise.js │ ├── install-dep.js │ ├── plugin-get-all.js │ ├── semver-validate.js │ └── util.js ├── plugin-get-config.js ├── plugin-init.js ├── plugin-new.js └── template │ ├── plugin-stage-config.js.ejs │ └── plugin │ ├── README.md.ejs │ ├── asset │ └── image │ │ └── logo.png │ ├── component │ └── component.vue.ejs │ ├── package.json.ejs │ ├── stage-config.js.ejs │ └── view │ ├── stage1.vue.ejs │ └── stage2.vue.ejs ├── src ├── app.vue ├── assets │ ├── image │ │ ├── error-page │ │ │ ├── 404.png │ │ │ └── logo.png │ │ ├── login │ │ │ ├── login-ba.png │ │ │ ├── login-btn.png │ │ │ ├── nickname.png │ │ │ ├── password.png │ │ │ └── team-name.png │ │ ├── logo.png │ │ ├── mobile-logo.png │ │ └── user │ │ │ ├── corner.png │ │ │ ├── user-bg.png │ │ │ ├── user.jpg │ │ │ └── user.png │ └── style │ │ ├── elementFont.css │ │ ├── fonts │ │ ├── element-icons.ttf │ │ └── element-icons.woff │ │ ├── index.scss │ │ ├── realize │ │ ├── animation.scss │ │ ├── element-variable.scss │ │ ├── mixin.scss │ │ ├── reset.scss │ │ ├── transition.scss │ │ └── variable.scss │ │ └── shared.scss ├── component │ ├── base │ │ ├── date-picker │ │ │ └── lin-date-picker.vue │ │ ├── dialog │ │ │ └── lin-dialog.vue │ │ ├── dropdown │ │ │ └── lin-dropdown.vue │ │ ├── icon │ │ │ └── lin-icon.vue │ │ ├── search │ │ │ └── lin-search.vue │ │ ├── source-code │ │ │ └── source-code.vue │ │ ├── sticky-top │ │ │ └── sticky-top.vue │ │ ├── tinymce │ │ │ └── index.vue │ │ └── upload-image │ │ │ ├── README.md │ │ │ ├── index.vue │ │ │ └── utils.js │ ├── layout │ │ ├── app-main.vue │ │ ├── avatar.vue │ │ ├── back-top.vue │ │ ├── breadcrumb.vue │ │ ├── clear-tab.vue │ │ ├── index.js │ │ ├── menu-tab.vue │ │ ├── nav-bar.vue │ │ ├── reuse-tab.vue │ │ ├── screen-full.vue │ │ ├── sidebar │ │ │ ├── logo.vue │ │ │ ├── menu-tree.vue │ │ │ ├── search.vue │ │ │ └── sidebar.vue │ │ └── user.vue │ └── notify │ │ ├── emitter.js │ │ ├── index.js │ │ ├── notify.vue │ │ └── observer.js ├── config │ ├── echarts.js │ ├── error-code.js │ ├── global.js │ ├── index.js │ └── stage │ │ ├── admin.js │ │ ├── book.js │ │ ├── case.js │ │ ├── center.js │ │ ├── hidden.js │ │ ├── index.js │ │ ├── jmeter.js │ │ ├── machine.js │ │ ├── plugin.js │ │ ├── project.js │ │ └── task.js ├── lin │ ├── context │ │ ├── admin.js │ │ └── index.js │ ├── directive │ │ ├── authorize.js │ │ └── index.js │ ├── filter │ │ └── index.js │ ├── model │ │ ├── admin.js │ │ ├── log.js │ │ ├── notify.js │ │ └── user.js │ ├── plugin │ │ ├── axios.js │ │ └── index.js │ └── util │ │ ├── auto-jump.js │ │ ├── cookie.js │ │ ├── date.js │ │ ├── emitter.js │ │ ├── index.js │ │ ├── search.js │ │ ├── sse.js │ │ ├── storage.js │ │ ├── token.js │ │ └── util.js ├── main.js ├── model │ └── book.js ├── plugin │ └── custom │ │ ├── assets │ │ └── image │ │ │ └── logo.png │ │ ├── package.json │ │ ├── stage-config.js │ │ └── view │ │ └── multiple-input.vue ├── router │ ├── home-router.js │ ├── index.js │ └── route.js ├── store │ ├── action.js │ ├── getter.js │ ├── index.js │ ├── mutation-type.js │ ├── mutation.js │ ├── socket.js │ └── state.js └── view │ ├── about │ └── about.vue │ ├── admin │ ├── group │ │ ├── group-create.vue │ │ ├── group-edit.vue │ │ ├── group-list.vue │ │ ├── group-permission.vue │ │ └── use-group.js │ └── user │ │ ├── use-user.js │ │ ├── user-create.vue │ │ ├── user-info.vue │ │ ├── user-list.vue │ │ └── user-password.vue │ ├── book │ ├── book-list.vue │ └── book.vue │ ├── case │ ├── case-debug.vue │ ├── case-list.vue │ ├── case.vue │ ├── detail.vue │ ├── qps-limit.vue │ └── task.vue │ ├── center │ └── center.vue │ ├── error-page │ └── 404.vue │ ├── home │ └── home.vue │ ├── jmeter │ ├── aggregate-report-record.vue │ └── aggregate-report-search.vue │ ├── log │ └── log.vue │ ├── login │ └── login.vue │ ├── machine │ ├── machine-list.vue │ └── machine.vue │ ├── project │ ├── project-list.vue │ └── project.vue │ └── task │ └── history-list.vue ├── tests └── unit │ └── .eslintrc.js └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | Pipfile.lock 2 | __pycache__ 3 | */__pycache__/ 4 | *.pytest_cache/ 5 | *.db 6 | *.pyc 7 | *.cpython-36.pyc 8 | secure.py 9 | 10 | 11 | .DS_Store 12 | node_modules 13 | miniprogram_npm 14 | dist 15 | /script/.cache 16 | build 17 | 18 | # local env files 19 | .env.local 20 | .env.*.local 21 | 22 | # Log files 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | *.log* 27 | 28 | # Editor directories and files 29 | .idea 30 | .vscode 31 | *.suo 32 | *.ntvs* 33 | *.njsproj 34 | *.sln 35 | *.sw* 36 | 37 | # cert 38 | *.pem 39 | www.* 40 | 41 | 20* 42 | upload 43 | download 44 | apache-jmeter -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | **/target/ 7 | config/ 8 | 9 | **.DS_Store 10 | 11 | ### STS ### 12 | .apt_generated 13 | .classpath 14 | .factorypath 15 | .project 16 | .settings 17 | .springBeans 18 | .sts4-cache 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbbuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | assets/ 37 | logs/ 38 | 39 | 40 | -------------------------------------------------------------------------------- /api/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/bo/FileBO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.bo; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author pedro@TaleLin 7 | * @author Juzi@TaleLin 8 | */ 9 | @Data 10 | public class FileBO { 11 | 12 | /** 13 | * 文件 id 14 | */ 15 | private Integer id; 16 | 17 | /** 18 | * 文件 key,上传时指定的 19 | */ 20 | private String key; 21 | 22 | /** 23 | * 文件路径 24 | */ 25 | private String path; 26 | 27 | /** 28 | * 文件 URL 29 | */ 30 | private String url; 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/bo/GroupPermissionBO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.bo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.GroupDO; 4 | import io.github.guojiaxing1995.easyJmeter.model.PermissionDO; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.springframework.beans.BeanUtils; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author pedro@TaleLin 14 | * @author Juzi@TaleLin 15 | * @author colorful@TaleLin 16 | */ 17 | @Data 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class GroupPermissionBO { 21 | private Integer id; 22 | 23 | private String name; 24 | 25 | private String info; 26 | 27 | private List permissions; 28 | 29 | public GroupPermissionBO(GroupDO group, List permissions) { 30 | BeanUtils.copyProperties(group, this); 31 | this.permissions = permissions; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/bo/LoginCaptchaBO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.bo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author Gadfly 9 | * @since 2021-11-19 15:20 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class LoginCaptchaBO { 15 | private String captcha; 16 | private Long expired; 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/LocalUser.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.UserDO; 4 | 5 | /** 6 | * 线程安全的当前登录用户,如果用户为登录,则得到 null 7 | * 8 | * @author pedro@TaleLin 9 | */ 10 | public class LocalUser { 11 | 12 | private static ThreadLocal local = new ThreadLocal<>(); 13 | 14 | /** 15 | * 得到当前登录用户 16 | * 17 | * @return user | null 18 | */ 19 | public static UserDO getLocalUser() { 20 | return LocalUser.local.get(); 21 | } 22 | 23 | /** 24 | * 设置登录用户 25 | * 26 | * @param user user 27 | */ 28 | public static void setLocalUser(UserDO user) { 29 | LocalUser.local.set(user); 30 | } 31 | 32 | public static T getLocalUser(Class clazz) { 33 | return (T) local.get(); 34 | } 35 | 36 | /** 37 | * 清理当前用户 38 | */ 39 | public static void clearLocalUser() { 40 | LocalUser.local.remove(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/CacheConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import com.github.benmanes.caffeine.cache.Cache; 4 | import com.github.benmanes.caffeine.cache.Caffeine; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | @Configuration 11 | public class CacheConfiguration { 12 | 13 | @Bean 14 | public Cache caffeineCache() { 15 | return Caffeine.newBuilder() 16 | // 设置最后一次写入或访问后经过固定时间过期 17 | .expireAfterWrite(3, TimeUnit.HOURS) 18 | // 初始的缓存空间大小 19 | .initialCapacity(100) 20 | // 缓存的最大条数 21 | .maximumSize(1000) 22 | .build(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/CodeMessageConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.PropertySource; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | */ 13 | @SuppressWarnings("ConfigurationProperties") 14 | @Component 15 | @ConfigurationProperties 16 | @PropertySource(value = "classpath:code-message.properties", encoding = "UTF-8") 17 | public class CodeMessageConfiguration { 18 | 19 | private static Map codeMessage = new HashMap<>(); 20 | 21 | public static String getMessage(Integer code) { 22 | return codeMessage.get(code); 23 | } 24 | 25 | public Map getCodeMessage() { 26 | return codeMessage; 27 | } 28 | 29 | public void setCodeMessage(Map codeMessage) { 30 | CodeMessageConfiguration.codeMessage = codeMessage; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/CustomServletModelAttributeMethodProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import org.springframework.util.Assert; 4 | import org.springframework.web.bind.ServletRequestDataBinder; 5 | import org.springframework.web.bind.WebDataBinder; 6 | import org.springframework.web.context.request.NativeWebRequest; 7 | import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; 8 | 9 | import javax.servlet.ServletRequest; 10 | 11 | /** 12 | * @author Gadfly 13 | */ 14 | 15 | public class CustomServletModelAttributeMethodProcessor extends ServletModelAttributeMethodProcessor { 16 | 17 | public CustomServletModelAttributeMethodProcessor(final boolean annotationNotRequired) { 18 | super(annotationNotRequired); 19 | } 20 | 21 | @Override 22 | protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) { 23 | ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class); 24 | Assert.state(servletRequest != null, "No ServletRequest"); 25 | ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder; 26 | 27 | // ServletModelAttributeMethodProcessor 此处使用的 servletBinder.bind(servletRequest) 28 | // 修改的目的是为了将 ServletRequestDataBinder 换成自定的 CustomServletRequestDataBinder 29 | new CustomServletRequestDataBinder(servletBinder.getTarget()).bind(servletRequest); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/CustomServletRequestDataBinder.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import com.baomidou.mybatisplus.core.toolkit.StringUtils; 4 | import org.springframework.beans.MutablePropertyValues; 5 | import org.springframework.beans.PropertyValue; 6 | import org.springframework.web.bind.ServletRequestDataBinder; 7 | 8 | import javax.servlet.ServletRequest; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author Gadfly 14 | */ 15 | 16 | public class CustomServletRequestDataBinder extends ServletRequestDataBinder { 17 | 18 | public CustomServletRequestDataBinder(final Object target) { 19 | super(target); 20 | } 21 | 22 | @Override 23 | protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) { 24 | List pvs = mpvs.getPropertyValueList(); 25 | List adds = new LinkedList<>(); 26 | for (PropertyValue pv : pvs) { 27 | String name = pv.getName(); 28 | String camel = StringUtils.underlineToCamel(name); 29 | if (!name.equals(camel)) { 30 | adds.add(new PropertyValue(camel, pv.getValue())); 31 | } 32 | } 33 | pvs.addAll(adds); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/InfluxDBConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import org.influxdb.InfluxDB; 4 | import org.influxdb.InfluxDBFactory; 5 | import org.influxdb.dto.Query; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class InfluxDBConfiguration { 12 | 13 | @Autowired 14 | private InfluxDBProperties influxDBProperties; 15 | 16 | @Bean 17 | public InfluxDB influxDB() { 18 | InfluxDB influxDB = InfluxDBFactory.connect(influxDBProperties.getUrl(), influxDBProperties.getUser(), influxDBProperties.getPassword()); 19 | influxDB.query(new Query("CREATE DATABASE " + influxDBProperties.getDatabase())); 20 | influxDB.setDatabase(influxDBProperties.getDatabase()); 21 | return influxDB; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/InfluxDBProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | @Data 9 | @ConfigurationProperties(prefix = "spring.influx") 10 | public class InfluxDBProperties { 11 | 12 | private String url; 13 | private String user; 14 | private String password; 15 | private String database; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 7 | 8 | @EnableWebSecurity 9 | public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 10 | 11 | @Value("${socket.server.enable}") 12 | private boolean enableServer; 13 | 14 | @Override 15 | protected void configure(HttpSecurity http) throws Exception { 16 | if (!enableServer) { 17 | http 18 | .authorizeRequests() 19 | .anyRequest().denyAll() 20 | .and() 21 | .csrf().disable(); 22 | } else { 23 | http 24 | .authorizeRequests() 25 | .anyRequest().permitAll() 26 | .and() 27 | .csrf().disable(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/configuration/SocketIOClientConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.configuration; 2 | 3 | import io.socket.client.IO; 4 | import io.socket.client.Socket; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import java.net.URISyntaxException; 11 | 12 | @Slf4j 13 | @Configuration(proxyBeanMethods = false) 14 | public class SocketIOClientConfiguration { 15 | 16 | @Value("${socket.client.serverUrl}") 17 | private String serverUrl; 18 | 19 | @Value("${socket.client.enable}") 20 | private boolean enableSocket; 21 | 22 | @Bean 23 | public Socket socketIOClient() throws URISyntaxException { 24 | IO.Options options = new IO.Options(); 25 | options.forceNew = true; 26 | options.reconnection = true; 27 | options.query = "client-type=machine"; 28 | 29 | Socket socket = IO.socket(serverUrl, options); 30 | 31 | if (enableSocket) { 32 | socket.connect(); 33 | log.info("SocketIO client connect to {}", serverUrl); 34 | } else { 35 | log.info("SocketIO client is disabled."); 36 | } 37 | 38 | return socket; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/constant/IdentityConstant.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.constant; 2 | 3 | /** 4 | * 身份认证常量 5 | * 6 | * @author pedro@TaleLin 7 | */ 8 | public class IdentityConstant { 9 | 10 | /** 11 | * 表示通过用户名和密码来进行身份认证 12 | */ 13 | public static final String USERNAME_PASSWORD_IDENTITY = "USERNAME_PASSWORD"; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/enumeration/DebugTypeEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.enumeration; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import com.baomidou.mybatisplus.annotation.IEnum; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | import lombok.Getter; 7 | 8 | @Getter 9 | public enum DebugTypeEnum implements IEnum { 10 | ERROR(0, "ERROR"), 11 | CONFIG(1, "CONFIG"), 12 | SAMPLE(2,"SAMPLE"), 13 | LOG(3,"LOG"); 14 | 15 | 16 | @EnumValue 17 | private final Integer value; 18 | 19 | @JsonValue 20 | private final String desc; 21 | 22 | DebugTypeEnum(Integer value, String desc) { 23 | this.value = value; 24 | this.desc = desc; 25 | } 26 | 27 | @Override 28 | public Integer getValue() { 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/enumeration/GroupLevelEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.enumeration; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.IEnum; 5 | 6 | /** 7 | * @author colorful@TaleLin 8 | * @author Juzi@TaleLin 9 | */ 10 | public enum GroupLevelEnum implements IEnum { 11 | /** 12 | * 超级管理员 13 | */ 14 | ROOT(1), 15 | /** 16 | * 游客 17 | */ 18 | GUEST(2), 19 | /** 20 | * 普通用户 21 | */ 22 | USER(3); 23 | 24 | private final Integer value; 25 | 26 | GroupLevelEnum(Integer value) { 27 | this.value = value; 28 | } 29 | 30 | /** 31 | * MybatisEnumTypeHandler 转换时调用此方法 32 | * 33 | * @return 枚举对应的 code 值 34 | * @see com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler 35 | */ 36 | @Override 37 | public Integer getValue() { 38 | return this.value; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/factory/YamlPropertySourceFactory.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.factory; 2 | 3 | import org.springframework.boot.env.YamlPropertySourceLoader; 4 | import org.springframework.core.env.PropertySource; 5 | import org.springframework.core.io.support.EncodedResource; 6 | import org.springframework.core.io.support.PropertySourceFactory; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | /** 12 | * @author Juzi@TaleLin 13 | */ 14 | public class YamlPropertySourceFactory implements PropertySourceFactory { 15 | @Override 16 | public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { 17 | List> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()); 18 | return sources.get(0); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/interceptor/RequestLogInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.interceptor; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.util.IPUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.servlet.AsyncHandlerInterceptor; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author colorful@TaleLin 13 | */ 14 | @Slf4j 15 | public class RequestLogInterceptor implements AsyncHandlerInterceptor { 16 | 17 | 18 | private ThreadLocal startTime = new ThreadLocal<>(); 19 | 20 | @Override 21 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 22 | startTime.set(System.currentTimeMillis()); 23 | return true; 24 | } 25 | 26 | @Override 27 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 28 | log.info("[{}] -> [{}] from: {} costs: {}ms", 29 | request.getMethod(), 30 | request.getServletPath(), 31 | IPUtil.getIPFromRequest(request), 32 | System.currentTimeMillis() - startTime.get() 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/jmeter/LinkStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.jmeter; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import io.github.guojiaxing1995.easyJmeter.model.TaskDO; 5 | 6 | public interface LinkStrategy { 7 | 8 | void setTask(TaskDO taskDO); 9 | 10 | Boolean reportSuccess() throws JsonProcessingException; 11 | 12 | Boolean reportFail(); 13 | 14 | Boolean interruptThread(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/jmeter/xstream/AssertionResult.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.jmeter.xstream; 2 | 3 | import com.thoughtworks.xstream.annotations.XStreamAlias; 4 | import com.thoughtworks.xstream.annotations.XStreamAsAttribute; 5 | import lombok.Data; 6 | 7 | @Data 8 | @XStreamAlias("assertionResult") 9 | public class AssertionResult { 10 | 11 | @XStreamAsAttribute 12 | private String name; 13 | 14 | @XStreamAsAttribute 15 | private Boolean failure; 16 | 17 | @XStreamAsAttribute 18 | private Boolean error; 19 | 20 | @XStreamAsAttribute 21 | private String failureMessage; 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/jmeter/xstream/JmeterLogDeal.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.jmeter.xstream; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.IOException; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | public class JmeterLogDeal { 10 | public static String[] cutContentFromFile(String inputFile, String startPattern, String endPattern) throws IOException { 11 | // 读取输入文件 12 | StringBuilder contentBuilder = new StringBuilder(); 13 | try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) { 14 | String line; 15 | while ((line = reader.readLine()) != null) { 16 | contentBuilder.append(line).append(System.lineSeparator()); 17 | } 18 | String content = contentBuilder.toString(); 19 | 20 | // 定义并应用正则表达式 21 | Pattern pattern = Pattern.compile(startPattern + "(.*?)" + endPattern, Pattern.DOTALL | Pattern.MULTILINE); 22 | Matcher matcher = pattern.matcher(content); 23 | 24 | if (matcher.find()) { 25 | String[] result = matcher.group(1).split("\n"); 26 | String[] logs= new String[result.length-1]; 27 | System.arraycopy(result, 0, logs, 0, result.length - 1); 28 | return logs; 29 | } else { 30 | return new String[]{}; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/jmeter/xstream/TestResults.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.jmeter.xstream; 2 | 3 | import com.thoughtworks.xstream.annotations.XStreamAlias; 4 | import com.thoughtworks.xstream.annotations.XStreamImplicit; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | @Data 10 | @XStreamAlias("testResults") 11 | public class TestResults { 12 | 13 | @XStreamImplicit 14 | @XStreamAlias("httpSample") 15 | private List httpSamples; 16 | } 17 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/serializer/DeserializerObjectMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.module.SimpleModule; 6 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 7 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | @Slf4j 11 | public class DeserializerObjectMapper { 12 | 13 | public static ObjectMapper getObjectMapper(){ 14 | ObjectMapper objectMapper = new ObjectMapper(); 15 | SimpleModule module = new SimpleModule(); 16 | module.addDeserializer(TaskResultEnum.class, new TaskResultEnumDeserializer()); 17 | module.addDeserializer(JmeterStatusEnum.class, new JmeterStatusEnumDeserializer()); 18 | objectMapper.registerModule(module); 19 | 20 | return objectMapper; 21 | } 22 | 23 | public static T deserialize(String jsonString, Class clazz) { 24 | try { 25 | return getObjectMapper().readValue(jsonString, clazz); 26 | } catch (JsonProcessingException e) { 27 | log.error("反序列化失败:" + jsonString, e); 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/serializer/JmeterStatusEnumDeserializer.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 8 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 9 | 10 | import java.io.IOException; 11 | 12 | public class JmeterStatusEnumDeserializer extends StdDeserializer { 13 | protected JmeterStatusEnumDeserializer(Class vc) { 14 | super(vc); 15 | } 16 | 17 | protected JmeterStatusEnumDeserializer() { 18 | this(null); 19 | } 20 | 21 | @Override 22 | public JmeterStatusEnum deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException { 23 | JsonNode node = parser.getCodec().readTree(parser); 24 | 25 | // 从 JSON 中读取 "value" 和 "desc" 字段的值 26 | int value = node.get("value").asInt(); 27 | String desc = node.get("desc").asText(); 28 | 29 | // 根据获取的值创建 TaskResultEnum 枚举实例 30 | return JmeterStatusEnum.fromValueAndDesc(value, desc); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/serializer/JmeterStatusEnumSerializer.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 7 | 8 | import java.io.IOException; 9 | 10 | public class JmeterStatusEnumSerializer extends JsonSerializer { 11 | @Override 12 | public void serialize(JmeterStatusEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 13 | gen.writeStartObject(); 14 | gen.writeFieldName("value"); 15 | gen.writeObject(value.getValue()); 16 | gen.writeFieldName("desc"); 17 | gen.writeObject(value.getDesc()); 18 | gen.writeEndObject(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/serializer/TaskResultEnumDeserializer.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonNode; 6 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 7 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 8 | 9 | import java.io.IOException; 10 | 11 | public class TaskResultEnumDeserializer extends StdDeserializer { 12 | 13 | public TaskResultEnumDeserializer() { 14 | this(null); 15 | } 16 | 17 | public TaskResultEnumDeserializer(Class vc) { 18 | super(vc); 19 | } 20 | 21 | @Override 22 | public TaskResultEnum deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { 23 | JsonNode node = parser.getCodec().readTree(parser); 24 | 25 | // 从 JSON 中读取 "value" 和 "desc" 字段的值 26 | int value = node.get("value").asInt(); 27 | String desc = node.get("desc").asText(); 28 | 29 | // 根据获取的值创建 TaskResultEnum 枚举实例 30 | return TaskResultEnum.fromValueAndDesc(value, desc); 31 | } 32 | } -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/serializer/TaskResultEnumSerializer.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.serializer; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | 8 | import java.io.IOException; 9 | 10 | public class TaskResultEnumSerializer extends JsonSerializer { 11 | @Override 12 | public void serialize(TaskResultEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 13 | gen.writeStartObject(); 14 | gen.writeFieldName("value"); 15 | gen.writeObject(value.getValue()); 16 | gen.writeFieldName("desc"); 17 | gen.writeObject(value.getDesc()); 18 | gen.writeEndObject(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/util/EnumUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.util; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.function.Predicate; 8 | 9 | 10 | public class EnumUtil { 11 | private static Map map = new ConcurrentHashMap<>(); 12 | 13 | /** 14 | * 根据条件获取枚举对象 15 | * 16 | * @param className 枚举类 17 | * @param predicate 筛选条件 18 | * @param 19 | * @return 20 | */ 21 | public static Optional getEnumObject(Class className, Predicate predicate) { 22 | if (!className.isEnum()) { 23 | return null; 24 | } 25 | Object obj = map.get(className); 26 | T[] ts = null; 27 | if (obj == null) { 28 | ts = className.getEnumConstants(); 29 | map.put(className, ts); 30 | } else { 31 | ts = (T[]) obj; 32 | } 33 | return Arrays.stream(ts).filter(predicate).findAny(); 34 | } 35 | 36 | 37 | } -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/common/util/PageUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.common.util; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.github.guojiaxing1995.easyJmeter.vo.PageResponseVO; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author colorful@TaleLin 10 | */ 11 | public class PageUtil { 12 | 13 | public static PageResponseVO build(IPage iPage) { 14 | return new PageResponseVO(Math.toIntExact(iPage.getTotal()), iPage.getRecords(), 15 | Math.toIntExact(iPage.getCurrent()), Math.toIntExact(iPage.getSize())); 16 | } 17 | 18 | public static PageResponseVO build(IPage iPage, List records) { 19 | return new PageResponseVO(Math.toIntExact(iPage.getTotal()), records, 20 | Math.toIntExact(iPage.getCurrent()), 21 | Math.toIntExact(iPage.getSize())); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/controller/cms/FileController.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.controller.cms; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.bo.FileBO; 4 | import io.github.guojiaxing1995.easyJmeter.service.FileService; 5 | import io.github.talelin.core.annotation.LoginRequired; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.util.MultiValueMap; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import org.springframework.web.multipart.MultipartFile; 12 | import org.springframework.web.multipart.MultipartHttpServletRequest; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * @author pedro@TaleLin 18 | * @author Juzi@TaleLin 19 | */ 20 | @RestController 21 | @RequestMapping("/cms/file") 22 | public class FileController { 23 | 24 | @Autowired 25 | private FileService fileService; 26 | 27 | /** 28 | * 文件上传 29 | * 30 | * @param multipartHttpServletRequest 携带文件的 request 31 | * @return 文件信息 32 | */ 33 | @PostMapping 34 | @LoginRequired 35 | public List upload(MultipartHttpServletRequest multipartHttpServletRequest) { 36 | MultiValueMap fileMap = 37 | multipartHttpServletRequest.getMultiFileMap(); 38 | return fileService.upload(fileMap); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/DispatchPermissionDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotNull; 6 | import javax.validation.constraints.Positive; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | */ 12 | @Data 13 | public class DispatchPermissionDTO { 14 | 15 | @Positive(message = "{group.id.positive}") 16 | @NotNull(message = "{group.id.not-null}") 17 | private Integer groupId; 18 | 19 | @Positive(message = "{permission.id.positive}") 20 | @NotNull(message = "{permission.id.not-null}") 21 | private Integer permissionId; 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/DispatchPermissionsDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotNull; 6 | import javax.validation.constraints.Positive; 7 | import java.util.List; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | @Data 14 | public class DispatchPermissionsDTO { 15 | 16 | @Positive(message = "{group.id.positive}") 17 | @NotNull(message = "{group.id.not-null}") 18 | private Integer groupId; 19 | 20 | private List permissionIds; 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/NewGroupDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import lombok.Data; 4 | import org.hibernate.validator.constraints.Length; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | import java.util.List; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | @Data 14 | public class NewGroupDTO { 15 | @NotBlank(message = "{group.name.not-blank}") 16 | @Length(min = 1, max = 60, message = "{group.name.length}") 17 | private String name; 18 | 19 | @Length(max = 255, message = "{group.info.length}") 20 | private String info; 21 | 22 | private List permissionIds; 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/QueryUsersDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.dto.query.BasePageDTO; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import javax.validation.constraints.Min; 8 | 9 | /** 10 | * @author Gadfly 11 | * @since 2021-06-28 18:48 12 | */ 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | public class QueryUsersDTO extends BasePageDTO { 16 | 17 | @Min(value = 1, message = "{group.id.positive}") 18 | private Integer groupId; 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/RemovePermissionsDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotNull; 6 | import javax.validation.constraints.Positive; 7 | import java.util.List; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | @Data 14 | public class RemovePermissionsDTO { 15 | 16 | @Positive(message = "{group.id.positive}") 17 | @NotNull(message = "{group.id.not-null}") 18 | private Integer groupId; 19 | 20 | private List permissionIds; 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/ResetPasswordDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import io.github.talelin.autoconfigure.validator.EqualField; 4 | import lombok.Data; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | import javax.validation.constraints.Pattern; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | */ 12 | @EqualField(srcField = "newPassword", dstField = "confirmPassword", message = "{password.equal-field}") 13 | @Data 14 | public class ResetPasswordDTO { 15 | 16 | @NotBlank(message = "{password.new.not-blank}") 17 | @Pattern(regexp = "^[A-Za-z0-9_*&$#@]{6,22}$", message = "{password.new.pattern}") 18 | private String newPassword; 19 | 20 | @NotBlank(message = "{password.confirm.not-blank}") 21 | private String confirmPassword; 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/UpdateGroupDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import lombok.Data; 4 | import org.hibernate.validator.constraints.Length; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | */ 11 | @Data 12 | public class UpdateGroupDTO { 13 | 14 | @NotBlank(message = "{group.name.not-blank}") 15 | @Length(min = 1, max = 60, message = "{group.name.length}") 16 | private String name; 17 | 18 | @Length(max = 255, message = "{group.info.length}") 19 | private String info; 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/admin/UpdateUserInfoDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.admin; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Min; 6 | import javax.validation.constraints.NotEmpty; 7 | import java.util.List; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | @Data 14 | public class UpdateUserInfoDTO { 15 | 16 | @NotEmpty(message = "{group.ids.not-empty}") 17 | private List<@Min(1) Integer> groupIds; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/book/CreateOrUpdateBookDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.book; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.hibernate.validator.constraints.Length; 6 | 7 | import javax.validation.constraints.NotEmpty; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | public class CreateOrUpdateBookDTO { 16 | 17 | @NotEmpty(message = "{book.title.not-empty}") 18 | @Length(max = 50, message = "{book.title.length}") 19 | private String title; 20 | 21 | @NotEmpty(message = "{book.author.not-empty}") 22 | @Length(max = 50, message = "{book.author.length}") 23 | private String author; 24 | 25 | @NotEmpty(message = "{book.summary.not-empty}") 26 | @Length(max = 1000, message = "{book.summary.length}") 27 | private String summary; 28 | 29 | @Length(max = 100, message = "{book.image.length}") 30 | private String image; 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/file/CreateFileDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.file; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @Data 7 | @NoArgsConstructor 8 | public class CreateFileDTO { 9 | 10 | private String name; 11 | 12 | private String type; 13 | 14 | private String path; 15 | 16 | private String url; 17 | 18 | private Long size; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/jcase/CaseDebugDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.jcase; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class CaseDebugDTO { 7 | private Integer caseId; 8 | 9 | private Long debugId; 10 | } 11 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/jcase/CreateOrUpdateCaseDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.jcase; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.hibernate.validator.constraints.Length; 6 | 7 | import javax.validation.constraints.NotEmpty; 8 | import javax.validation.constraints.NotNull; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | public class CreateOrUpdateCaseDTO { 13 | 14 | @NotEmpty(message = "{case.name.not-empty}") 15 | @Length(max = 50, message = "{case.name.length}") 16 | private String name; 17 | 18 | @NotNull(message = "{case.project.not-empty}") 19 | private Integer project; 20 | 21 | @NotNull(message = "{case.jmx.not-empty}") 22 | private String jmx; 23 | 24 | private String csv; 25 | 26 | private String jar; 27 | 28 | @Length(max = 500, message = "{case.description.length}") 29 | private String description; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/log/QueryLogDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.log; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.dto.query.BasePageDTO; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.format.annotation.DateTimeFormat; 7 | 8 | import java.util.Date; 9 | 10 | /** 11 | * @author colorful@TaleLin 12 | */ 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | public class QueryLogDTO extends BasePageDTO { 16 | 17 | protected static Integer defaultCount = 12; 18 | 19 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 20 | private Date start; 21 | 22 | @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 23 | private Date end; 24 | 25 | private String name; 26 | 27 | private String keyword; 28 | 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/machine/CreateOrUpdateMachineDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.machine; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.hibernate.validator.constraints.Length; 6 | 7 | import javax.validation.constraints.NotEmpty; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | public class CreateOrUpdateMachineDTO { 12 | 13 | @NotEmpty(message = "{machine.name.not-empty}") 14 | @Length(max = 50, message = "{machine.name.length}") 15 | private String name; 16 | 17 | @NotEmpty(message = "{machine.address.not-empty}") 18 | @Length(max = 100, message = "{machine.address.length}") 19 | private String address; 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/machine/HeartBeatMachineDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.machine; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class HeartBeatMachineDTO implements Serializable { 13 | 14 | private static final long serialVersionUID = 877827989345852198L; 15 | 16 | private String clientId; 17 | 18 | private String address; 19 | 20 | private String path; 21 | 22 | private String version; 23 | 24 | private Boolean isOnline; 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/project/CreateOrUpdateProjectDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.project; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.hibernate.validator.constraints.Length; 6 | 7 | import javax.validation.constraints.NotEmpty; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | public class CreateOrUpdateProjectDTO { 12 | 13 | @NotEmpty(message = "{project.name.not-empty}") 14 | @Length(max = 50, message = "{project.name.length}") 15 | private String name; 16 | 17 | @Length(max = 1000, message = "{project.description.length}") 18 | private String description; 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/query/BasePageDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.query; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.Max; 6 | import javax.validation.constraints.Min; 7 | 8 | /** 9 | * @author Gadfly 10 | */ 11 | 12 | @Data 13 | public class BasePageDTO { 14 | 15 | @Min(value = 1, message = "{page.count.min}") 16 | @Max(value = 30, message = "{page.count.max}") 17 | private Integer count; 18 | 19 | @Min(value = 0, message = "{page.number.min}") 20 | private Integer page; 21 | 22 | public Integer getCount() { 23 | if (null == count) { 24 | return 10; 25 | } 26 | return count; 27 | } 28 | 29 | public Integer getPage() { 30 | if (null == page) { 31 | return 0; 32 | } 33 | return page; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/CreateOrUpdateTaskDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.hibernate.validator.constraints.Length; 6 | 7 | import javax.validation.constraints.Min; 8 | import javax.validation.constraints.NotNull; 9 | import java.util.ArrayList; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | public class CreateOrUpdateTaskDTO { 14 | 15 | @NotNull(message = "{task.case.not-empty}") 16 | private Integer jCase; 17 | 18 | @NotNull(message = "{task.threads.not-empty}") 19 | @Min(message = "{task.threads.min}", value = 1) 20 | private Integer numThreads; 21 | 22 | @NotNull(message = "{task.duration.not-empty}") 23 | @Min(message = "{task.duration.min}", value = 1) 24 | private Integer duration; 25 | 26 | private Integer rampTime; 27 | 28 | @Min(message = "{task.qpsLimit.min}", value = 0) 29 | private Integer qpsLimit; 30 | 31 | private ArrayList machine; 32 | 33 | @NotNull(message = "{task.machine-num.not-empty}") 34 | @Min(message = "{task.machine-num.min}", value = 1) 35 | private Integer machineNum; 36 | 37 | @Min(message = "{task.granularity.min}", value = 0) 38 | private Integer granularity; 39 | 40 | private Boolean realtime; 41 | 42 | private Integer logLevel; 43 | 44 | @Length(max = 500, message = "{task.remark.length}") 45 | private String remark; 46 | } 47 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/JmeterAggregateReportDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.AggregateReportDO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.NotEmpty; 9 | import javax.validation.constraints.NotNull; 10 | import java.util.List; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class JmeterAggregateReportDTO { 16 | @NotNull(message = "{project.id.not-empty}") 17 | private Integer projectId; 18 | 19 | @NotEmpty(message = "{task.aggregate-reports.not-empty}") 20 | private List aggregateReports; 21 | 22 | private String text; 23 | } -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/JmeterParamDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class JmeterParamDTO { 11 | 12 | private String startTime; 13 | 14 | private String endTime; 15 | 16 | private String application; 17 | 18 | private String tags; 19 | 20 | private String text; 21 | 22 | private Integer projectId; 23 | 24 | private String label; 25 | 26 | } -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/ModifyTaskDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import javax.validation.constraints.Min; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | public class ModifyTaskDTO { 11 | 12 | private String taskId; 13 | 14 | @Min(message = "{task.qpsLimit.min}", value = 0) 15 | private Integer qpsLimit; 16 | } 17 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/TaskMachineDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.TaskDO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | public class TaskMachineDTO { 12 | 13 | private TaskDO taskDO; 14 | 15 | private String machineIp; 16 | 17 | private Boolean result; 18 | 19 | private Integer status; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/TaskProgressMachineDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class TaskProgressMachineDTO implements Serializable { 13 | 14 | private static final long serialVersionUID = 7592545643989464081L; 15 | private String taskId; 16 | private String machineIp; 17 | private Integer process; 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/TaskRealTimeDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @Data 7 | @NoArgsConstructor 8 | public class TaskRealTimeDTO { 9 | 10 | private String taskId; 11 | 12 | private String type; 13 | } 14 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/task/TaskSearchDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.task; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import javax.validation.constraints.NotNull; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | public class TaskSearchDTO { 11 | 12 | private String taskId; 13 | 14 | private String jCase; 15 | 16 | private Integer result; 17 | 18 | private String startTime; 19 | 20 | private String endTime; 21 | 22 | @NotNull(message = "{page.number}") 23 | private Integer page; 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/user/ChangePasswordDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.user; 2 | 3 | import io.github.talelin.autoconfigure.validator.EqualField; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.NotBlank; 8 | import javax.validation.constraints.Pattern; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | @EqualField(srcField = "newPassword", dstField = "confirmPassword", message = "{password.equal-field}") 17 | public class ChangePasswordDTO { 18 | 19 | @NotBlank(message = "{password.new.not-blank}") 20 | @Pattern(regexp = "^[A-Za-z0-9_*&$#@]{6,22}$", message = "{password.new.pattern}") 21 | private String newPassword; 22 | 23 | @NotBlank(message = "{password.confirm.not-blank}") 24 | private String confirmPassword; 25 | 26 | @NotBlank(message = "{password.old.not-blank}") 27 | private String oldPassword; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/user/LoginDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.user; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import javax.validation.constraints.NotBlank; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | public class LoginDTO { 15 | 16 | @NotBlank(message = "{username.not-blank}") 17 | private String username; 18 | 19 | @NotBlank(message = "{password.new.not-blank}") 20 | private String password; 21 | 22 | private String captcha; 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/user/RegisterDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.user; 2 | 3 | import io.github.talelin.autoconfigure.validator.EqualField; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.hibernate.validator.constraints.Length; 7 | 8 | import javax.validation.constraints.Email; 9 | import javax.validation.constraints.NotBlank; 10 | import javax.validation.constraints.Pattern; 11 | import java.util.List; 12 | 13 | /** 14 | * @author pedro@TaleLin 15 | * @author Juzi@TaleLin 16 | */ 17 | @Data 18 | @NoArgsConstructor 19 | @EqualField(srcField = "password", dstField = "confirmPassword", message = "{password.equal-field}") 20 | public class RegisterDTO { 21 | 22 | @NotBlank(message = "{username.not-blank}") 23 | @Length(min = 2, max = 10, message = "{username.length}") 24 | private String username; 25 | 26 | private List groupIds; 27 | 28 | @Email(message = "{email}") 29 | private String email; 30 | 31 | @NotBlank(message = "{password.new.not-blank}") 32 | @Pattern(regexp = "^[A-Za-z0-9_*&$#@]{6,22}$", message = "{password.new.pattern}") 33 | private String password; 34 | 35 | @NotBlank(message = "{password.confirm.not-blank}") 36 | private String confirmPassword; 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/dto/user/UpdateInfoDTO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.dto.user; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.hibernate.validator.constraints.Length; 6 | 7 | import javax.validation.constraints.Email; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | */ 12 | @NoArgsConstructor 13 | @Data 14 | public class UpdateInfoDTO { 15 | 16 | @Email(message = "{email}") 17 | private String email; 18 | 19 | @Length(min = 2, max = 10, message = "{nickname.length}") 20 | private String nickname; 21 | 22 | @Length(min = 2, max = 10, message = "{username.length}") 23 | private String username; 24 | 25 | @Length(max = 500, message = "{avatar.length}") 26 | private String avatar; 27 | } 28 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/extension/file/UploaderConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.extension.file; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.module.file.Uploader; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.annotation.Order; 8 | 9 | /** 10 | * 文件上传配置类 11 | * 12 | * @author Juzi@TaleLin 13 | * @author colorful@TaleLin 14 | */ 15 | @Configuration(proxyBeanMethods = false) 16 | public class UploaderConfiguration { 17 | /** 18 | * @return 本地文件上传实现类 19 | */ 20 | @Bean 21 | @Order 22 | @ConditionalOnMissingBean 23 | public Uploader uploader(){ 24 | return new LocalUploader(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/extension/file/config.yml: -------------------------------------------------------------------------------- 1 | #文件上传配置 2 | 3 | lin: 4 | file: 5 | # 文件服务域名 6 | domain: http://localhost:5000/ 7 | # 排除文件类型 8 | exclude: 9 | # 包括文件类型 10 | include: 11 | - .jpg 12 | - .png 13 | - .jpeg 14 | # 文件最大数量 15 | nums: 10 16 | # 服务器文件路径 17 | serve-path: assets/** 18 | # 单个文件最大体积 19 | single-limit: 2MB 20 | # 本地文件保存位置 21 | store-dir: assets/ 22 | spring: 23 | servlet: 24 | multipart: 25 | # 总体文件最大体积(只能从max-file-size设置总体文件的大小) 26 | max-file-size: 300MB 27 | max-request-size: 300MB 28 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/BookMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.BookDO; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | */ 13 | @Repository 14 | public interface BookMapper extends BaseMapper { 15 | 16 | List selectByTitleLikeKeyword(@Param("q") String q); 17 | 18 | List selectByTitle(@Param("title") String title); 19 | 20 | // @Select("SELECT * FROM book WHERE title=${q}") 21 | List selectByTitleKey(String q); 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/CaseMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.CaseDO; 5 | import io.github.guojiaxing1995.easyJmeter.vo.CaseInfoVO; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | @Repository 11 | public interface CaseMapper extends BaseMapper { 12 | List select(Integer id); 13 | 14 | List selectByProject(Integer project); 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/FileMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.FileDO; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | */ 11 | @Repository 12 | public interface FileMapper extends BaseMapper { 13 | 14 | FileDO selectByMd5(@Param("md5") String md5); 15 | 16 | int selectCountByMd5(@Param("md5") String md5); 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/GroupMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.GroupDO; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | */ 14 | @Repository 15 | public interface GroupMapper extends BaseMapper { 16 | 17 | /** 18 | * 获得用户的所有分组 19 | * 20 | * @param userId 用户id 21 | * @return 所有分组 22 | */ 23 | List selectGroupsByUserId(@Param("userId") Integer userId); 24 | 25 | /** 26 | * 获得用户的所有分组id 27 | * 28 | * @param userId 用户id 29 | * @return 所有分组id 30 | */ 31 | List selectUserGroupIds(@Param("userId") Integer userId); 32 | 33 | /** 34 | * 通过id获得分组个数 35 | * 36 | * @param id id 37 | * @return 个数 38 | */ 39 | int selectCountById(@Param("id") Integer id); 40 | 41 | /** 42 | * 检查用户是否在该名称的分组里 43 | * 44 | * @param userId 用户id 45 | * @param groupName 分组名 46 | */ 47 | int selectCountUserByUserIdAndGroupName(@Param("userId") Integer userId, @Param("groupName") String groupName); 48 | } 49 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/GroupPermissionMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.GroupPermissionDO; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | */ 14 | @Repository 15 | public interface GroupPermissionMapper extends BaseMapper { 16 | 17 | int insertBatch(@Param("relations") List relations); 18 | 19 | int deleteBatchByGroupIdAndPermissionId(@Param("groupId") Integer groupId, @Param("permissionIds") List permissionIds); 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/JFileMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.JFileDO; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface JFileMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/LogMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.github.guojiaxing1995.easyJmeter.common.mybatis.Page; 6 | import io.github.guojiaxing1995.easyJmeter.model.LogDO; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * @author pedro@TaleLin 13 | */ 14 | @Repository 15 | public interface LogMapper extends BaseMapper { 16 | 17 | IPage findLogsByUsernameAndRange(Page pager, String name, Date start, Date end); 18 | 19 | IPage searchLogsByUsernameAndKeywordAndRange(Page pager, String name, String keyword, Date start, Date end); 20 | 21 | IPage getUserNames(Page pager); 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/MachineMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.github.guojiaxing1995.easyJmeter.common.mybatis.Page; 6 | import io.github.guojiaxing1995.easyJmeter.model.MachineDO; 7 | import org.apache.ibatis.annotations.Param; 8 | import org.springframework.stereotype.Repository; 9 | 10 | import java.util.ArrayList; 11 | 12 | @Repository 13 | public interface MachineMapper extends BaseMapper { 14 | 15 | IPage selectByName(Page page, @Param("name") String name); 16 | 17 | MachineDO selectByAddress(String address); 18 | 19 | MachineDO selectByClientId(String clientId); 20 | 21 | ArrayList selectAll(); 22 | 23 | ArrayList executable(); 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/PermissionMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.PermissionDO; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | */ 14 | @Repository 15 | public interface PermissionMapper extends BaseMapper { 16 | 17 | /** 18 | * 通过分组ids得到所有分组下的权限 19 | * 20 | * @param groupIds 分组ids 21 | * @return 权限 22 | */ 23 | List selectPermissionsByGroupIds(@Param("groupIds") List groupIds); 24 | 25 | /** 26 | * 通过分组id得到所有分组下的权限 27 | * 28 | * @param groupId 分组id 29 | * @return 权限 30 | */ 31 | List selectPermissionsByGroupId(@Param("groupId") Integer groupId); 32 | 33 | /** 34 | * 通过分组ids得到所有分组下的权限 35 | * 36 | * @param groupIds 分组ids 37 | * @param module 权限模块 38 | * @return 权限 39 | */ 40 | List selectPermissionsByGroupIdsAndModule(@Param("groupIds") List groupIds, @Param("module") String module); 41 | } 42 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/ProjectMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.github.guojiaxing1995.easyJmeter.common.mybatis.Page; 6 | import io.github.guojiaxing1995.easyJmeter.model.ProjectDO; 7 | import org.apache.ibatis.annotations.Param; 8 | import org.springframework.stereotype.Repository; 9 | 10 | import java.util.List; 11 | 12 | @Repository 13 | public interface ProjectMapper extends BaseMapper { 14 | 15 | IPage selectByName(Page page, @Param("name") String name); 16 | 17 | List getAll(); 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/TaskLogMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 5 | import io.github.guojiaxing1995.easyJmeter.model.TaskLogDO; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | @Repository 11 | public interface TaskLogMapper extends BaseMapper { 12 | 13 | List search(String taskId, Integer jCase, JmeterStatusEnum status, String address, Boolean result); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/TaskMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.github.guojiaxing1995.easyJmeter.common.mybatis.Page; 6 | import io.github.guojiaxing1995.easyJmeter.model.TaskDO; 7 | import io.github.guojiaxing1995.easyJmeter.vo.HistoryTaskVO; 8 | import org.apache.ibatis.annotations.Param; 9 | import org.springframework.stereotype.Repository; 10 | 11 | @Repository 12 | public interface TaskMapper extends BaseMapper { 13 | 14 | IPage selectHistory(Page page, @Param("taskId") String taskId, 15 | @Param("startTime") String startTime, @Param("endTime") String endTime, 16 | @Param("result") Integer result, @Param("jmeterCase") String jmeterCase); 17 | 18 | Integer selectSumDuration(@Param("taskId") String taskId); 19 | 20 | TaskDO selectByIdIncludeDelete(@Param("id") Integer id); 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/UserGroupMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.UserGroupDO; 5 | import org.apache.ibatis.annotations.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | */ 13 | @Repository 14 | public interface UserGroupMapper extends BaseMapper { 15 | 16 | int insertBatch(@Param("relations") List relations); 17 | 18 | int deleteByUserId(@Param("user_id") Integer userId); 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/UserIdentityMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import io.github.guojiaxing1995.easyJmeter.model.UserIdentityDO; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * @author pedro@TaleLin 9 | */ 10 | @Repository 11 | public interface UserIdentityMapper extends BaseMapper { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import io.github.guojiaxing1995.easyJmeter.common.mybatis.Page; 6 | import io.github.guojiaxing1995.easyJmeter.model.UserDO; 7 | import org.springframework.stereotype.Repository; 8 | 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author colorful@TaleLin 13 | * @author Juzi@TaleLin 14 | */ 15 | @Repository 16 | public interface UserMapper extends BaseMapper { 17 | 18 | /** 19 | * 查询用户名为$username的人数 20 | * 21 | * @param username 用户名 22 | * @return 人数 23 | */ 24 | int selectCountByUsername(String username); 25 | 26 | /** 27 | * 查询用户id为$id的人数 28 | * 29 | * @param id 用户id 30 | * @return 人数 31 | */ 32 | int selectCountById(Integer id); 33 | 34 | /** 35 | * 通过分组id分页获取用户数据 36 | * 37 | * @param pager 分页 38 | * @param groupId 分组id 39 | * @param rootGroupId 超级用户组id(不返回超级用户组的用户) 40 | * @return 分页数据 41 | */ 42 | IPage selectPageByGroupId(Page pager, Integer groupId, Integer rootGroupId); 43 | } 44 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/BaseModel.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableLogic; 7 | import com.fasterxml.jackson.annotation.JsonFormat; 8 | import com.fasterxml.jackson.annotation.JsonIgnore; 9 | import lombok.Data; 10 | 11 | import java.util.Date; 12 | 13 | /** 14 | * @author Juzi@TaleLin 15 | */ 16 | @Data 17 | public class BaseModel { 18 | @TableId(value = "id", type = IdType.AUTO) 19 | private Integer id; 20 | 21 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai") 22 | private Date createTime; 23 | 24 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai") 25 | @TableField(update = "now()") 26 | private Date updateTime; 27 | 28 | @TableLogic 29 | @JsonIgnore 30 | private Date deleteTime; 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/BookDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | @Data 14 | @TableName("book") 15 | @EqualsAndHashCode(callSuper = true) 16 | public class BookDO extends BaseModel implements Serializable { 17 | 18 | private static final long serialVersionUID = 3531805912578317266L; 19 | 20 | private String title; 21 | 22 | private String author; 23 | 24 | private String summary; 25 | 26 | private String image; 27 | } 28 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/CaseDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | import java.io.Serializable; 9 | 10 | @Data 11 | @TableName("`case`") 12 | @EqualsAndHashCode(callSuper = true) 13 | public class CaseDO extends BaseModel implements Serializable { 14 | 15 | private static final long serialVersionUID = -1597190968205712177L; 16 | 17 | private String name; 18 | 19 | private Integer creator; 20 | 21 | private JmeterStatusEnum status; 22 | 23 | private Integer project; 24 | 25 | private String jmx; 26 | 27 | private String csv; 28 | 29 | private String jar; 30 | 31 | private String description; 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/FileDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | @Data 14 | @TableName("lin_file") 15 | @EqualsAndHashCode(callSuper = true) 16 | public class FileDO extends BaseModel implements Serializable { 17 | 18 | private static final long serialVersionUID = -3203293656352763490L; 19 | 20 | private String path; 21 | 22 | /** 23 | * LOCAL REMOTE 24 | */ 25 | private String type; 26 | 27 | private String name; 28 | 29 | private String extension; 30 | 31 | private Integer size; 32 | 33 | /** 34 | * md5值,防止上传重复文件 35 | */ 36 | private String md5; 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/GroupDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.GroupLevelEnum; 6 | import lombok.*; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | * @author Jokky@TaleLin 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @TableName(value = "lin_group") 20 | @EqualsAndHashCode(callSuper = true) 21 | public class GroupDO extends BaseModel implements Serializable { 22 | 23 | private static final long serialVersionUID = -8994898895671436007L; 24 | 25 | /** 26 | * 分组名称,例如:搬砖者 27 | */ 28 | private String name; 29 | 30 | /** 31 | * 分组信息:例如:搬砖的人 32 | */ 33 | private String info; 34 | 35 | /** 36 | * 分组级别(root、guest、user,其中 root、guest 分组只能存在一个) 37 | */ 38 | @TableField(value = "`level`") 39 | private GroupLevelEnum level; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/GroupPermissionDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | */ 14 | @Data 15 | @TableName("lin_group_permission") 16 | public class GroupPermissionDO implements Serializable { 17 | 18 | private static final long serialVersionUID = -358487811336536495L; 19 | 20 | @TableId(value = "id", type = IdType.AUTO) 21 | private Integer id; 22 | 23 | /** 24 | * 分组id 25 | */ 26 | private Integer groupId; 27 | 28 | /** 29 | * 权限id 30 | */ 31 | private Integer permissionId; 32 | 33 | public GroupPermissionDO(Integer groupId, Integer permissionId) { 34 | this.groupId = groupId; 35 | this.permissionId = permissionId; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/JFileDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @TableName("file") 11 | @EqualsAndHashCode(callSuper = true) 12 | public class JFileDO extends BaseModel implements Serializable { 13 | 14 | private static final long serialVersionUID = -3402162550338356318L; 15 | 16 | private String name; 17 | 18 | private String type; 19 | 20 | private String path; 21 | 22 | private String url; 23 | 24 | private Long size; 25 | 26 | private Boolean cut; 27 | 28 | private Integer originId; 29 | 30 | private String taskId; 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/LogDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.*; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @TableName("lin_log") 17 | @EqualsAndHashCode(callSuper = true) 18 | public class LogDO extends BaseModel implements Serializable { 19 | 20 | private static final long serialVersionUID = -7471474245124682011L; 21 | 22 | private String message; 23 | 24 | private Integer userId; 25 | 26 | private String username; 27 | 28 | private Integer statusCode; 29 | 30 | private String method; 31 | 32 | private String path; 33 | 34 | private String permission; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/MachineDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 7 | import lombok.*; 8 | 9 | import java.io.Serializable; 10 | 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @TableName(value = "machine") 16 | @EqualsAndHashCode(callSuper = true) 17 | public class MachineDO extends BaseModel implements Serializable { 18 | 19 | private static final long serialVersionUID = -6928506417680888594L; 20 | 21 | private String name; 22 | 23 | private String address; 24 | 25 | private String path; 26 | 27 | private String version; 28 | 29 | private Boolean isOnline; 30 | 31 | @TableField(value = "`jmeter_status`") 32 | private JmeterStatusEnum jmeterStatus; 33 | 34 | @JsonIgnore 35 | @TableField(value = "`client_id`") 36 | private String clientId; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/PermissionDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.*; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | */ 12 | @Data 13 | @Builder 14 | @TableName("lin_permission") 15 | @EqualsAndHashCode(callSuper = true) 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class PermissionDO extends BaseModel implements Serializable { 19 | 20 | private static final long serialVersionUID = -2400022443732120128L; 21 | 22 | /** 23 | * 权限名称,例如:访问首页 24 | */ 25 | private String name; 26 | 27 | /** 28 | * 权限所属模块,例如:人员管理 29 | */ 30 | private String module; 31 | 32 | /** 33 | * 0:关闭 1:开启 34 | */ 35 | private Boolean mount; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/ProjectDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.io.Serializable; 8 | 9 | 10 | @Data 11 | @TableName("project") 12 | @EqualsAndHashCode(callSuper = true) 13 | public class ProjectDO extends BaseModel implements Serializable { 14 | 15 | private static final long serialVersionUID = -483068736478817178L; 16 | 17 | private String name; 18 | 19 | private Integer creator; 20 | 21 | private String description; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/ReportDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.alibaba.fastjson2.JSONObject; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 7 | import lombok.Data; 8 | import org.springframework.data.mongodb.core.mapping.Document; 9 | import org.springframework.data.annotation.Id; 10 | 11 | import java.io.Serializable; 12 | import java.util.Date; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | @Data 17 | @Document(collection = "report") 18 | public class ReportDO implements Serializable { 19 | 20 | private static final long serialVersionUID = 4026728833071475370L; 21 | 22 | @Id 23 | private String taskId; 24 | 25 | private Map> dashBoardData; 26 | 27 | private Map graphData; 28 | 29 | private JFileDO file; 30 | 31 | private Integer caseId; 32 | 33 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai") 34 | private Date createTime; 35 | 36 | private TaskResultEnum result; 37 | 38 | @JsonIgnore 39 | private Date deleteTime; 40 | } 41 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/TaskDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.LogLevelEnum; 5 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | import java.io.Serializable; 10 | 11 | @Data 12 | @TableName("task") 13 | @EqualsAndHashCode(callSuper = true) 14 | public class TaskDO extends BaseModel implements Serializable { 15 | 16 | private static final long serialVersionUID = -7038882032711810177L; 17 | 18 | private String taskId; 19 | 20 | private Integer creator; 21 | 22 | private Integer jmeterCase; 23 | 24 | private String jmx; 25 | 26 | private String csv; 27 | 28 | private String jar; 29 | 30 | private Integer numThreads; 31 | 32 | private Integer duration; 33 | 34 | private Integer rampTime; 35 | 36 | private Integer qpsLimit; 37 | 38 | private String machine; 39 | 40 | private Integer machineNum; 41 | 42 | private Integer granularity; 43 | 44 | private Boolean realtime; 45 | 46 | private LogLevelEnum logLevel; 47 | 48 | private String remark; 49 | 50 | private TaskResultEnum result; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/TaskLogDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.io.Serializable; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @TableName("task_log") 16 | @EqualsAndHashCode(callSuper = true) 17 | public class TaskLogDO extends BaseModel implements Serializable { 18 | 19 | private static final long serialVersionUID = 177261585529036980L; 20 | 21 | private String taskId; 22 | 23 | private Integer jCase; 24 | 25 | private JmeterStatusEnum status; 26 | 27 | private Boolean result; 28 | 29 | private String address; 30 | 31 | private Integer machine; 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/UserDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.*; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @TableName("lin_user") 17 | @EqualsAndHashCode(callSuper = true) 18 | public class UserDO extends BaseModel implements Serializable { 19 | 20 | private static final long serialVersionUID = -1463999384554707735L; 21 | 22 | /** 23 | * 用户名,唯一 24 | */ 25 | private String username; 26 | 27 | /** 28 | * 用户昵称 29 | */ 30 | private String nickname; 31 | 32 | /** 33 | * 头像url 34 | */ 35 | private String avatar; 36 | 37 | /** 38 | * 邮箱 39 | */ 40 | private String email; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/UserGroupDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | */ 14 | @Data 15 | @TableName("lin_user_group") 16 | public class UserGroupDO implements Serializable { 17 | 18 | private static final long serialVersionUID = -7219009955825484511L; 19 | 20 | @TableId(value = "id", type = IdType.AUTO) 21 | private Integer id; 22 | 23 | /** 24 | * 用户id 25 | */ 26 | private Integer userId; 27 | 28 | /** 29 | * 分组id 30 | */ 31 | private Integer groupId; 32 | 33 | public UserGroupDO(Integer userId, Integer groupId) { 34 | this.userId = userId; 35 | this.groupId = groupId; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/model/UserIdentityDO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.*; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | */ 12 | @Setter 13 | @Getter 14 | @ToString 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @TableName("lin_user_identity") 19 | public class UserIdentityDO extends BaseModel implements Serializable { 20 | 21 | private static final long serialVersionUID = 456555840105356178L; 22 | 23 | /** 24 | * 用户id 25 | */ 26 | private Integer userId; 27 | 28 | /** 29 | * 认证类型,例如 username_password,用户名-密码认证 30 | */ 31 | private String identityType; 32 | 33 | /** 34 | * 认证,例如 用户名 35 | */ 36 | private String identifier; 37 | 38 | /** 39 | * 凭证,例如 密码 40 | */ 41 | private String credential; 42 | } 43 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/file/File.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.file; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * @author pedro@TaleLin 7 | */ 8 | @Setter 9 | @Getter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Builder 13 | public class File { 14 | 15 | /** 16 | * 真实url 17 | */ 18 | private String url; 19 | 20 | /** 21 | * 前端上传的key 22 | */ 23 | private String key; 24 | 25 | /** 26 | * 若 local,表示文件路径 27 | */ 28 | private String path; 29 | 30 | /** 31 | * LOCAL REMOTE 32 | */ 33 | private String type; 34 | 35 | /** 36 | * 文件名称 37 | */ 38 | private String name; 39 | 40 | /** 41 | * 扩展名,例:.jpg 42 | */ 43 | private String extension; 44 | 45 | /** 46 | * 文件大小 47 | */ 48 | private Integer size; 49 | 50 | /** 51 | * md5值,防止上传重复文件 52 | */ 53 | private String md5; 54 | } 55 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/file/FileConstant.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.file; 2 | 3 | /** 4 | * 文件相关常量值 5 | * 6 | * @author pedro@TaleLin 7 | */ 8 | public class FileConstant { 9 | 10 | /** 11 | * 本地文件 12 | */ 13 | public static final String LOCAL = "LOCAL"; 14 | 15 | /** 16 | * 远程文件,例如OSS 17 | */ 18 | public static final String REMOTE = "REMOTE"; 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/file/UploadHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.file; 2 | 3 | /** 4 | * 文件前预处理器 5 | * 6 | * @author pedro@TaleLin 7 | */ 8 | public interface UploadHandler { 9 | 10 | /** 11 | * 在文件写入本地或者上传到云之前调用此方法 12 | * 13 | * @return 是否上传,若返回false,则不上传 14 | */ 15 | boolean preHandle(File file); 16 | 17 | /** 18 | * 在文件写入本地或者上传到云之后调用此方法 19 | */ 20 | void afterHandle(File file); 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/file/Uploader.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.file; 2 | 3 | import org.springframework.util.MultiValueMap; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 文件上传服务接口 10 | * 11 | * @author pedro@TaleLin 12 | */ 13 | public interface Uploader { 14 | 15 | /** 16 | * 上传文件 17 | * 18 | * @param fileMap 文件map 19 | * @return 文件数据 20 | */ 21 | List upload(MultiValueMap fileMap); 22 | 23 | /** 24 | * 上传文件 25 | * 26 | * @param fileMap 文件map 27 | * @param uploadHandler 预处理器 28 | * @return 文件数据 29 | */ 30 | List upload(MultiValueMap fileMap, UploadHandler uploadHandler); 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/log/JmeterLogFilter.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.log; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | import ch.qos.logback.core.filter.Filter; 5 | import ch.qos.logback.core.spi.FilterReply; 6 | 7 | public class JmeterLogFilter extends Filter { 8 | 9 | @Override 10 | public FilterReply decide(ILoggingEvent event) { 11 | String logMessage = event.getMessage(); 12 | String loggerName = event.getLoggerName(); 13 | if (loggerName.startsWith("org.apache.jmeter") || 14 | logMessage.contains("debug_start") || logMessage.contains("debug_end")) { 15 | return FilterReply.ACCEPT; // 接受来自org.apache.jmeter 的所有日志 16 | } else { 17 | return FilterReply.DENY; // 过滤掉其他来源的日志 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/log/MDCAccessConstant.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.log; 2 | 3 | /** 4 | * @author Juzi@TaleLin 5 | * @date 2020/6/20 10:22 6 | */ 7 | public class MDCAccessConstant { 8 | public static final String REQUEST_METHOD_MDC_KEY = "req.method"; 9 | public static final String RESPONSE_STATUS_MDC_KEY = "res.status"; 10 | public static final String REQUEST_REFERER_MDC_KEY = "req.referer"; 11 | public static final String REQUEST_PROTOCOL_MDC_KEY = "req.protocol"; 12 | public static final String REQUEST_USER_AGENT_MDC_KEY = "req.userAgent"; 13 | public static final String REQUEST_REMOTE_HOST_MDC_KEY = "req.remoteHost"; 14 | public static final String REQUEST_REMOTE_ADDR_MDC_KEY = "req.remoteAddr"; 15 | public static final String REQUEST_REQUEST_URI_MDC_KEY = "req.requestURI"; 16 | public static final String REQUEST_REQUEST_URL_MDC_KEY = "req.requestURL"; 17 | public static final String REQUEST_QUERY_STRING_MDC_KEY = "req.queryString"; 18 | public static final String REQUEST_X_FORWARDED_FOR_MDC_KEY = "req.xForwardedFor"; 19 | public static final String REQUEST_BODY_BYTES_SENT_MDC_KEY = "req.bodyBytesSent"; 20 | public static final String REQUEST_REMOTE_PORT_MDC_KEY = "req.remotePort"; 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/message/MessageConstant.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.message; 2 | 3 | /** 4 | * websocket 模块常量 5 | * 6 | * @author pedro@TaleLin 7 | */ 8 | public class MessageConstant { 9 | public static final String USER_KEY = "user"; 10 | } 11 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/module/message/MessageWebSocketHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.module.message; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.socket.*; 5 | 6 | /** 7 | * @author pedro@TaleLin 8 | */ 9 | public class MessageWebSocketHandler implements WebSocketHandler { 10 | 11 | @Autowired 12 | private WsHandler wsHandler; 13 | 14 | @Override 15 | public void afterConnectionEstablished(WebSocketSession session) throws Exception { 16 | wsHandler.handleOpen(session); 17 | } 18 | 19 | @Override 20 | public void handleMessage(WebSocketSession session, WebSocketMessage webSocketMessage) throws Exception { 21 | if (webSocketMessage instanceof TextMessage) { 22 | TextMessage textMessage = (TextMessage) webSocketMessage; 23 | wsHandler.handleMessage(session, textMessage.getPayload()); 24 | } 25 | } 26 | 27 | @Override 28 | public void handleTransportError(WebSocketSession session, Throwable throwable) throws Exception { 29 | wsHandler.handleError(session, throwable); 30 | } 31 | 32 | @Override 33 | public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { 34 | wsHandler.handleClose(session); 35 | } 36 | 37 | @Override 38 | public boolean supportsPartialMessages() { 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/repository/AggregateReportRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.repository; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.AggregateReportDO; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | import org.springframework.data.mongodb.repository.Query; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public interface AggregateReportRepository extends MongoRepository { 11 | @Query("{'projectId': ?0, 'text': { $regex: ?1, $options: 'i' }, 'label': { $regex: ?2, $options: 'i' }, 'deleteTime': { $exists: false }}") 12 | Optional> getAggregateReportRecord(Integer projectId, String text, String label); 13 | } -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/repository/StatisticsRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.repository; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.StatisticsDO; 4 | import org.springframework.data.mongodb.repository.Aggregation; 5 | import org.springframework.data.mongodb.repository.MongoRepository; 6 | 7 | import java.util.List; 8 | 9 | public interface StatisticsRepository extends MongoRepository { 10 | 11 | @Aggregation({ 12 | "{ $match: { id: { $not: /manual/ } } }", 13 | "{ $group: { _id: null, totalSamplesSum: { $sum: '$samplesSum' } } }", 14 | "{ $project: { _id: 0, totalSamplesSum: 1 } }" 15 | }) 16 | List getTotalSamplesSumForNonManual(); 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/BookService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.dto.book.CreateOrUpdateBookDTO; 4 | import io.github.guojiaxing1995.easyJmeter.model.BookDO; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | */ 12 | public interface BookService { 13 | 14 | boolean createBook(CreateOrUpdateBookDTO validator); 15 | 16 | List getBookByKeyword(String q); 17 | 18 | boolean updateBook(BookDO book, CreateOrUpdateBookDTO validator); 19 | 20 | BookDO getById(Integer id); 21 | 22 | List findAll(); 23 | 24 | boolean deleteById(Integer id); 25 | 26 | List getBookByTitleKey(String q); 27 | } 28 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/CaseService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 4 | import io.github.guojiaxing1995.easyJmeter.dto.jcase.CaseDebugDTO; 5 | import io.github.guojiaxing1995.easyJmeter.dto.jcase.CreateOrUpdateCaseDTO; 6 | import io.github.guojiaxing1995.easyJmeter.model.CaseDO; 7 | import io.github.guojiaxing1995.easyJmeter.vo.CaseInfoPlusVO; 8 | import io.github.guojiaxing1995.easyJmeter.vo.CaseInfoVO; 9 | 10 | import java.io.IOException; 11 | import java.util.List; 12 | 13 | public interface CaseService { 14 | 15 | boolean createCase(CreateOrUpdateCaseDTO caseDTO); 16 | 17 | boolean updateCase(CaseDO caseDO, CreateOrUpdateCaseDTO caseDTO); 18 | 19 | CaseDO getById(Integer id); 20 | 21 | boolean deleteCase(Integer id); 22 | 23 | List selectCase(Integer id); 24 | 25 | CaseInfoPlusVO getCaseInfoById(Integer id); 26 | 27 | boolean updateCaseStatus(CaseDO caseDO, JmeterStatusEnum status); 28 | 29 | void debugCase(CaseDebugDTO caseDebugDTO) throws IOException; 30 | } 31 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/CommonService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.StatisticsDO; 4 | 5 | import java.util.Map; 6 | 7 | public interface CommonService { 8 | 9 | Map getEnum(); 10 | 11 | Map getTotal(); 12 | 13 | StatisticsDO getStatisticsById(String id); 14 | } 15 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/FileService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import io.github.guojiaxing1995.easyJmeter.bo.FileBO; 5 | import io.github.guojiaxing1995.easyJmeter.model.FileDO; 6 | import org.springframework.util.MultiValueMap; 7 | import org.springframework.web.multipart.MultipartFile; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author pedro@TaleLin 13 | */ 14 | public interface FileService extends IService { 15 | 16 | /** 17 | * 上传文件 18 | * 19 | * @param fileMap 文件map 20 | * @return 文件数据 21 | */ 22 | List upload(MultiValueMap fileMap); 23 | 24 | /** 25 | * 通过md5检查文件是否存在 26 | * 27 | * @param md5 md5 28 | * @return true 表示已存在 29 | */ 30 | boolean checkFileExistByMd5(String md5); 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/JFileService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.JFileDO; 4 | import io.github.guojiaxing1995.easyJmeter.vo.CutFileVO; 5 | import io.github.guojiaxing1995.easyJmeter.vo.JFileVO; 6 | import org.springframework.web.multipart.MultipartFile; 7 | 8 | import java.io.IOException; 9 | import java.io.OutputStream; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public interface JFileService { 14 | 15 | JFileVO createFile(MultipartFile file); 16 | 17 | Boolean setFileCut(Integer id, Boolean cut); 18 | 19 | String downloadFile(Integer id, String dir); 20 | 21 | void downloadCutFile(List cutFileVOList, String dir); 22 | 23 | List createCsvCutFiles(Map> fileMp); 24 | 25 | JFileDO searchById(Integer id); 26 | 27 | Boolean needCut(String[] fileIds); 28 | 29 | JFileDO createFile(String filePath); 30 | 31 | Boolean updateById(JFileDO jFileDO); 32 | 33 | void downLoadJmeterLogZip(String taskId, OutputStream outputStream) throws IOException; 34 | 35 | List searchJtlByTaskId(String taskId); 36 | 37 | String getStoreDir(); 38 | } 39 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/MachineService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 5 | import io.github.guojiaxing1995.easyJmeter.dto.machine.CreateOrUpdateMachineDTO; 6 | import io.github.guojiaxing1995.easyJmeter.dto.machine.HeartBeatMachineDTO; 7 | import io.github.guojiaxing1995.easyJmeter.model.MachineDO; 8 | 9 | import java.util.ArrayList; 10 | 11 | public interface MachineService { 12 | 13 | IPage getMachineByName(Integer current, String name); 14 | 15 | boolean createMachine(CreateOrUpdateMachineDTO validator); 16 | 17 | boolean updateMachine(MachineDO machineDO, CreateOrUpdateMachineDTO validator); 18 | 19 | MachineDO getById(Integer id); 20 | 21 | boolean deleteMachine(Integer id); 22 | 23 | void setMachineStatus(HeartBeatMachineDTO heartBeatMachineDTO, Boolean online); 24 | 25 | MachineDO getByClientId(String clientId); 26 | 27 | ArrayList getAll(); 28 | 29 | boolean updateMachineStatus(MachineDO machineDO, JmeterStatusEnum status); 30 | 31 | MachineDO getByAddress(String address); 32 | 33 | void setMachineOffline(); 34 | } 35 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/ProjectService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import io.github.guojiaxing1995.easyJmeter.dto.project.CreateOrUpdateProjectDTO; 5 | import io.github.guojiaxing1995.easyJmeter.model.ProjectDO; 6 | 7 | import java.util.List; 8 | 9 | 10 | public interface ProjectService { 11 | 12 | IPage getProjectByName(Integer current, String name); 13 | 14 | boolean createProject(CreateOrUpdateProjectDTO validator); 15 | 16 | boolean updateProject(ProjectDO projectDO, CreateOrUpdateProjectDTO validator); 17 | 18 | ProjectDO getById(Integer id); 19 | 20 | boolean deleteProject(Integer id); 21 | 22 | List getAll(); 23 | 24 | Integer getCaseCount(Integer id); 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/TaskInfluxdbService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.dto.task.JmeterParamDTO; 4 | 5 | import java.time.OffsetDateTime; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public interface TaskInfluxdbService { 10 | 11 | Map getTimes(String taskId); 12 | 13 | Map sampleCounts(String taskId, String startTime, String endTime); 14 | 15 | Map throughputGraph(String taskId, String startTime, String endTime, List points); 16 | 17 | Map errorGraph(String taskId, String startTime, String endTime, List points); 18 | 19 | Map errorInfo(String taskId, String startTime, String endTime); 20 | 21 | List getEvents(JmeterParamDTO jmeterParamDTO); 22 | 23 | List> getAggregateReport(List jmeterParamDTOList); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/TaskLogService.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 4 | import io.github.guojiaxing1995.easyJmeter.model.TaskLogDO; 5 | 6 | import java.util.List; 7 | 8 | public interface TaskLogService { 9 | 10 | List getTaskLog(String taskId, Integer jCase, JmeterStatusEnum status, String address, Boolean result); 11 | 12 | boolean createTaskLog(TaskLogDO taskLogDO); 13 | 14 | boolean updateTaskLog(TaskLogDO taskLogDO, Boolean result); 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/service/impl/TaskLogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.service.impl; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 4 | import io.github.guojiaxing1995.easyJmeter.mapper.TaskLogMapper; 5 | import io.github.guojiaxing1995.easyJmeter.model.TaskLogDO; 6 | import io.github.guojiaxing1995.easyJmeter.service.TaskLogService; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Slf4j 14 | @Service 15 | public class TaskLogServiceImpl implements TaskLogService { 16 | 17 | @Autowired 18 | private TaskLogMapper taskLogMapper; 19 | 20 | @Override 21 | public List getTaskLog(String taskId, Integer jCase, JmeterStatusEnum status, String address, Boolean result) { 22 | return taskLogMapper.search(taskId,jCase,status,address,result); 23 | } 24 | 25 | @Override 26 | public boolean createTaskLog(TaskLogDO taskLogDO) { 27 | return taskLogMapper.insert(taskLogDO) > 0; 28 | } 29 | 30 | @Override 31 | public boolean updateTaskLog(TaskLogDO taskLogDO, Boolean result) { 32 | taskLogDO.setResult(result); 33 | return taskLogMapper.updateById(taskLogDO) > 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/CaseDebugVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.DebugTypeEnum; 4 | import io.github.guojiaxing1995.easyJmeter.common.jmeter.xstream.TestResults; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | @Data 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class CaseDebugVO { 15 | private Integer caseId; 16 | private Long debugId; 17 | private DebugTypeEnum type; 18 | private TestResults result; 19 | private String log; 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/CaseInfoVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 6 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.Date; 13 | import java.util.HashMap; 14 | 15 | @Data 16 | @Builder 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class CaseInfoVO { 20 | 21 | private Integer id; 22 | 23 | private String name; 24 | 25 | private JmeterStatusEnum status; 26 | 27 | private String projectName; 28 | 29 | private Integer project; 30 | 31 | private String jmx; 32 | 33 | private String csv; 34 | 35 | private String jar; 36 | 37 | private String description; 38 | 39 | private String creator; 40 | 41 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai") 42 | private Date createTime; 43 | 44 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai") 45 | private Date updateTime; 46 | 47 | @JsonIgnore 48 | private Date deleteTime; 49 | 50 | private String taskId; 51 | 52 | private TaskResultEnum taskResult; 53 | 54 | private HashMap taskProgress; 55 | } 56 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/CreatedVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.util.ResponseUtil; 4 | import io.github.talelin.autoconfigure.bean.Code; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * @author colorful@TaleLin 9 | */ 10 | public class CreatedVO extends UnifyResponseVO { 11 | 12 | public CreatedVO() { 13 | super(Code.CREATED.getCode()); 14 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); 15 | } 16 | 17 | public CreatedVO(int code) { 18 | super(code); 19 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); 20 | } 21 | 22 | public CreatedVO(String message) { 23 | super(message); 24 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); 25 | } 26 | 27 | public CreatedVO(int code, String message) { 28 | super(code, message); 29 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return super.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/CutFileVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.JFileDO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.springframework.beans.BeanUtils; 9 | 10 | @Data 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class CutFileVO { 15 | 16 | private Integer id; 17 | 18 | private String name; 19 | 20 | private String url; 21 | 22 | private Integer originId; 23 | 24 | private String taskId; 25 | 26 | private String originName; 27 | 28 | public CutFileVO(JFileDO fileDO, String originName) { 29 | BeanUtils.copyProperties(fileDO, this); 30 | this.originName = originName; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/DeletedVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.util.ResponseUtil; 4 | import io.github.talelin.autoconfigure.bean.Code; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * @author colorful@TaleLin 9 | */ 10 | public class DeletedVO extends UnifyResponseVO { 11 | 12 | public DeletedVO() { 13 | super(Code.DELETED.getCode()); 14 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 15 | } 16 | 17 | public DeletedVO(int code) { 18 | super(code); 19 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 20 | } 21 | 22 | public DeletedVO(String message) { 23 | super(message); 24 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 25 | } 26 | 27 | public DeletedVO(int code, String message) { 28 | super(code, message); 29 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return super.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/HistoryTaskVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.Date; 11 | 12 | @Data 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class HistoryTaskVO { 17 | 18 | private Integer id; 19 | 20 | private String taskId; 21 | 22 | private String creator; 23 | 24 | private String jmeterCase; 25 | 26 | private Integer caseId; 27 | 28 | private TaskResultEnum result; 29 | 30 | private Integer numThreads; 31 | 32 | private Integer duration; 33 | 34 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai") 35 | private Date createTime; 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/JFileVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.JFileDO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.springframework.beans.BeanUtils; 9 | 10 | @Data 11 | @Builder 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class JFileVO { 15 | 16 | private Integer id; 17 | 18 | private String name; 19 | 20 | private String type; 21 | 22 | private String path; 23 | 24 | private String url; 25 | 26 | private String size; 27 | 28 | private Boolean cut; 29 | 30 | public JFileVO(JFileDO fileDO, String size) { 31 | BeanUtils.copyProperties(fileDO, this); 32 | this.size = size; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/LoginCaptchaVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author Gadfly 9 | */ 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class LoginCaptchaVO { 14 | /** 15 | * 加密后的验证码 16 | */ 17 | private String tag; 18 | /** 19 | * 验证码图片地址,可使用base64 20 | */ 21 | private String image; 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/MachineCutFileVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.TaskDO; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | @Data 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class MachineCutFileVO { 17 | private Map> machineDOCutFileVOListMap; 18 | 19 | private TaskDO taskDO; 20 | 21 | private Boolean needCut; 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/PageResponseVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 分页数据统一 view object 12 | * 13 | * @author pedro@TaleLin 14 | */ 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @Builder 19 | public class PageResponseVO { 20 | 21 | private Integer total; 22 | 23 | private List items; 24 | 25 | private Integer page; 26 | 27 | private Integer count; 28 | } 29 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/ProjectInfoVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import io.github.guojiaxing1995.easyJmeter.model.ProjectDO; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import org.springframework.beans.BeanUtils; 10 | 11 | import java.util.Date; 12 | 13 | @Data 14 | @Builder 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class ProjectInfoVO { 18 | 19 | private Integer id; 20 | 21 | private String name; 22 | 23 | private String creator; 24 | 25 | private String description; 26 | 27 | private Integer caseNum; 28 | 29 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone="Asia/Shanghai") 30 | private Date createTime; 31 | 32 | public ProjectInfoVO(ProjectDO projectDO, String username, Integer caseNum){ 33 | BeanUtils.copyProperties(projectDO, this); 34 | this.creator = username; 35 | this.caseNum = caseNum; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/TaskProgressVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.JmeterStatusEnum; 4 | import io.github.guojiaxing1995.easyJmeter.common.enumeration.TaskResultEnum; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.HashMap; 11 | 12 | @Data 13 | @Builder 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class TaskProgressVO { 17 | 18 | private String taskId; 19 | 20 | private JmeterStatusEnum status; 21 | 22 | private HashMap taskProgress; 23 | 24 | private TaskResultEnum taskResult; 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/UpdatedVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.common.util.ResponseUtil; 4 | import io.github.talelin.autoconfigure.bean.Code; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * @author pedro@TaleLin 9 | */ 10 | public class UpdatedVO extends UnifyResponseVO { 11 | 12 | public UpdatedVO() { 13 | super(Code.UPDATED.getCode()); 14 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 15 | } 16 | 17 | public UpdatedVO(int code) { 18 | super(code); 19 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 20 | } 21 | 22 | public UpdatedVO(String message) { 23 | super(message); 24 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 25 | } 26 | 27 | public UpdatedVO(int code, String message) { 28 | super(code, message); 29 | ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return super.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/UserInfoVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.GroupDO; 4 | import io.github.guojiaxing1995.easyJmeter.model.UserDO; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import org.springframework.beans.BeanUtils; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * 用户信息 view object 15 | * 16 | * @author pedro@TaleLin 17 | * @author colorful@TaleLin 18 | */ 19 | @Data 20 | @Builder 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | public class UserInfoVO { 24 | 25 | private Integer id; 26 | 27 | /** 28 | * 用户名,唯一 29 | */ 30 | private String username; 31 | 32 | /** 33 | * 用户昵称 34 | */ 35 | private String nickname; 36 | 37 | /** 38 | * 头像url 39 | */ 40 | private String avatar; 41 | 42 | /** 43 | * 邮箱 44 | */ 45 | private String email; 46 | 47 | /** 48 | * 分组 49 | */ 50 | private List groups; 51 | 52 | public UserInfoVO(UserDO user, List groups) { 53 | BeanUtils.copyProperties(user, this); 54 | this.groups = groups; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /api/src/main/java/io/github/guojiaxing1995/easyJmeter/vo/UserPermissionVO.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter.vo; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.model.UserDO; 4 | import lombok.Data; 5 | import org.springframework.beans.BeanUtils; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * 用户 + 权限 view object 12 | * 13 | * @author pedro@TaleLin 14 | * @author Juzi@TaleLin 15 | */ 16 | @Data 17 | public class UserPermissionVO { 18 | 19 | private Integer id; 20 | 21 | private String nickname; 22 | 23 | private String avatar; 24 | 25 | private Boolean admin; 26 | 27 | private String email; 28 | 29 | private List>>> permissions; 30 | 31 | public UserPermissionVO() { 32 | } 33 | 34 | public UserPermissionVO(UserDO userDO, List>>> permissions) { 35 | BeanUtils.copyProperties(userDO, this); 36 | this.permissions = permissions; 37 | } 38 | 39 | public UserPermissionVO(UserDO userDO) { 40 | BeanUtils.copyProperties(userDO, this); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api/src/main/resources/DejaVuSerif-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/api/src/main/resources/DejaVuSerif-Bold.ttf -------------------------------------------------------------------------------- /api/src/main/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:11-jre 2 | 3 | MAINTAINER guojiaxing<302802003@qq.com> 4 | 5 | EXPOSE 5000 6 | 7 | COPY ./target/*.jar /opt/easyJmeter.jar 8 | 9 | COPY ./src/main/resources/docker-entrypoint.sh /opt/docker-entrypoint.sh 10 | 11 | WORKDIR /opt/ 12 | 13 | ENV JMETER_HOME=/opt/apache-jmeter 14 | 15 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone 16 | 17 | RUN apt-get update && apt-get install -y procps 18 | 19 | ENTRYPOINT ["sh", "./docker-entrypoint.sh"] 20 | 21 | CMD ["server", "prod"] 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /api/src/main/resources/apache-jmeter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/api/src/main/resources/apache-jmeter.zip -------------------------------------------------------------------------------- /api/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #主配置文件 2 | 3 | spring: 4 | application: 5 | name: easy-jmeter 6 | # 激活环境配置 7 | profiles: 8 | active: dev 9 | # 文件编码 UTF8 10 | mandatory-file-encoding: UTF-8 11 | mvc: 12 | # 404 交给异常处理器处理 13 | throw-exception-if-no-handler-found: true 14 | web: 15 | resources: 16 | # 关闭默认静态资源的映射规则 17 | add-mappings: false 18 | 19 | mybatis-plus: 20 | configuration: 21 | # 开启下划线转驼峰 22 | map-underscore-to-camel-case: true 23 | # 指定默认枚举类型的类型转换器 24 | default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler 25 | global-config: 26 | # 开启/关闭 banner 打印 27 | banner: false 28 | db-config: 29 | # 逻辑删除(软删除) 30 | logic-delete-value: NOW() 31 | logic-not-delete-value: 'NULL' 32 | # mapper路径位置 33 | mapper-locations: classpath:mapper/*.xml 34 | 35 | 36 | lin: 37 | cms: 38 | # 开启行为日志记录(logger) 39 | logger-enabled: true 40 | # access token 过期时间,3600s 一个小时 41 | token-access-expire: 3600 42 | # refresh token 过期时间,2592000s 一个月 43 | token-refresh-expire: 2592000 44 | # 令牌 secret 45 | token-secret: x88Wf0991079889x8796a0Ac68f9ecJJU17c5Vbe8beod7d8d3e695*4 46 | logging: 47 | logback: 48 | rollingpolicy: 49 | max-history: 50 | config: 51 | 52 | swagger: 53 | enable: true 54 | application-name: ${spring.application.name} 55 | application-version: 1.0 56 | application-description: 接口文档 57 | try-host: http://localhost:${server.port} -------------------------------------------------------------------------------- /api/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _________ _______ __ ____ _________________________ 2 | / ____/ | / ___/\ \/ / / / |/ / ____/_ __/ ____/ __ \ 3 | / __/ / /| | \__ \ \ / __ / / /|_/ / __/ / / / __/ / /_/ / 4 | / /___/ ___ |___/ / / / / /_/ / / / / /___ / / / /___/ _, _/ 5 | /_____/_/ |_/____/ /_/ \____/_/ /_/_____/ /_/ /_____/_/ |_| 6 | -------------------------------------------------------------------------------- /api/src/main/resources/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/sh 3 | 4 | set -e 5 | 6 | if [ $1 = "server" ];then 7 | java -jar -Dsocket.server.enable=true -Dsocket.client.enable=false easyJmeter.jar --spring.profiles.active=$2 8 | elif [ $1 = "agent" ];then 9 | java -jar -Dsocket.server.enable=false -Dsocket.client.enable=true easyJmeter.jar --spring.profiles.active=$2 10 | else 11 | echo "参数错误 1) server 作为后端主程序启动. 2)agent 作为压力机监听器启动" 12 | exit 1 13 | fi 14 | -------------------------------------------------------------------------------- /api/src/main/resources/mapper/FileMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 27 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /api/src/main/resources/mapper/GroupPermissionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | INSERT INTO lin_group_permission(group_id, permission_id) 15 | VALUES 16 | 17 | (#{relation.groupId}, #{relation.permissionId}) 18 | 19 | 20 | 21 | 22 | DELETE FROM lin_group_permission 23 | WHERE group_id = #{groupId} 24 | AND permission_id IN 25 | 26 | #{permissionId} 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /api/src/main/resources/mapper/JFileMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /api/src/main/resources/mapper/ProjectMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 22 | 23 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /api/src/main/resources/mapper/UserGroupMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | INSERT INTO lin_user_group(user_id, group_id) 14 | VALUES 15 | 16 | (#{relation.userId}, #{relation.groupId}) 17 | 18 | 19 | 20 | 21 | DELETE FROM lin_user_group 22 | WHERE user_id = #{user_id} 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /api/src/main/resources/mapper/UserIdentityMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /api/src/test/java/Jackson2Test.java: -------------------------------------------------------------------------------- 1 | import com.baomidou.mybatisplus.core.toolkit.StringUtils; 2 | import com.fasterxml.jackson.annotation.JsonFormat; 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | @Slf4j 12 | public class Jackson2Test { 13 | 14 | @Test 15 | public void testMap() throws JsonProcessingException { 16 | ObjectMapper mapper = new ObjectMapper(); 17 | //mapper.setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE); 18 | //mapper.configOverride(Map.Entry.class).setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.OBJECT)); 19 | mapper.configOverride(Map.Entry.class).setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING)); 20 | Map res = new HashMap<>(); 21 | res.put("username", "pedro"); 22 | res.put("userAge", 24); 23 | String s = mapper.writeValueAsString(res); 24 | log.info(s); 25 | } 26 | 27 | @Test 28 | public void testCamel() throws JsonProcessingException { 29 | String str = "userAge"; 30 | String s = StringUtils.camelToUnderline(str); 31 | log.info(s); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/src/test/java/io/github/guojiaxing1995/easyJmeter/LatticyApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.guojiaxing1995.easyJmeter; 2 | 3 | import io.github.guojiaxing1995.easyJmeter.module.file.FileProperties; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.context.ActiveProfiles; 8 | 9 | @SpringBootTest 10 | @ActiveProfiles("test") 11 | public class LatticyApplicationTests { 12 | 13 | @Autowired 14 | private FileProperties fileProperties; 15 | 16 | @Test 17 | public void contextLoads() { 18 | System.out.println(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /api/src/test/resources/mpg/templates/mapper.xml.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <#if enableCache> 6 | 7 | 8 | 9 | 10 | <#if baseResultMap> 11 | 12 | 13 | <#list table.fields as field> 14 | <#if field.keyFlag><#--生成主键排在第一位--> 15 | 16 | 17 | 18 | <#list table.commonFields as field><#--生成公共字段 --> 19 | <#if field.keyFlag> 20 | 21 | <#else> 22 | 23 | 24 | 25 | <#list table.fields as field> 26 | <#if !field.keyFlag><#--生成普通字段 --> 27 | 28 | 29 | 30 | 31 | 32 | 33 | <#if baseColumnList> 34 | 35 | 36 | <#list table.commonFields as field> 37 | ${field.name}, 38 | 39 | ${table.fieldNames} 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /web/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /web/.env.development: -------------------------------------------------------------------------------- 1 | ENV = 'development' 2 | 3 | VUE_APP_BASE_URL = 'http://127.0.0.1:5000/' 4 | 5 | VUE_APP_SOCKETIO_URL = 'http://127.0.0.1:9000/' 6 | 7 | # VUE_APP_BASE_URL = 'http://192.168.56.66:8037/' 8 | 9 | # VUE_APP_SOCKETIO_URL = 'http://192.168.56.66:8036/' 10 | -------------------------------------------------------------------------------- /web/.env.production: -------------------------------------------------------------------------------- 1 | 2 | 3 | VUE_APP_BASE_URL = '/' 4 | 5 | VUE_APP_SOCKETIO_URL = '/' 6 | -------------------------------------------------------------------------------- /web/.eslintignore: -------------------------------------------------------------------------------- 1 | /builds/ 2 | /public/ 3 | /dist/ 4 | /script/.cache 5 | /*.js 6 | /node_modules/ 7 | /tests/unit/LIcon.test.js 8 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /script/.cache 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /web/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, //字符串是否使用单引号,默认为false,使用双引号 3 | semi: false, //行位是否使用分号,默认为true 4 | trailingComma: 'all', //是否使用尾逗号,有三个可选值"" 5 | printWidth: 120, 6 | arrowParens: 'avoid', 7 | } 8 | -------------------------------------------------------------------------------- /web/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | addons: 5 | chrome: stable 6 | sudo: required 7 | before_script: 8 | - "sudo chown root /opt/google/chrome/chrome-sandbox" 9 | - "sudo chmod 4755 /opt/google/chrome/chrome-sandbox" 10 | script: npm run test:unit -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.22-alpine-slim 2 | 3 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone 4 | 5 | COPY dist/ /usr/share/nginx/html/ 6 | 7 | COPY default.conf /etc/nginx/conf.d/ 8 | -------------------------------------------------------------------------------- /web/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /web/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [['@vue/cli-plugin-babel/preset']], 3 | plugins: [ 4 | //去除element babel 新版直接手动导入即可 5 | '@babel/plugin-proposal-optional-chaining', 6 | '@babel/plugin-proposal-nullish-coalescing-operator', 7 | ], 8 | } 9 | -------------------------------------------------------------------------------- /web/default.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | server { 5 | listen 80; 6 | 7 | client_max_body_size 800m; 8 | 9 | location / { 10 | root /usr/share/nginx/html; 11 | index index.html index.htm; 12 | } 13 | 14 | location ~ ^/v1/* { 15 | proxy_pass http://server:5000; 16 | proxy_read_timeout 3000; 17 | } 18 | 19 | location ~ ^/cms/* { 20 | proxy_pass http://server:5000; 21 | proxy_read_timeout 3000; 22 | } 23 | 24 | location ~ ^/assets/* { 25 | proxy_pass http://server:5000; 26 | proxy_read_timeout 3000; 27 | } 28 | 29 | location ~ ^/socket.io/* { 30 | proxy_pass http://server:9000; 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /web/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'js', 4 | 'jsx', 5 | 'json', 6 | 'vue' 7 | ], 8 | transform: { 9 | '^.+\\.vue$': 'vue-jest', 10 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 11 | '^.+\\.jsx?$': 'babel-jest' 12 | }, 13 | transformIgnorePatterns: [ 14 | '/node_modules/' 15 | ], 16 | moduleNameMapper: { 17 | '^@/(.*)$': '/src/$1' 18 | }, 19 | snapshotSerializers: [ 20 | 'jest-serializer-vue' 21 | ], 22 | testMatch: [ 23 | '**/tests/unit/**/*.test.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 24 | ], 25 | testURL: 'http://localhost/', 26 | watchPlugins: [ 27 | 'jest-watch-typeahead/filename', 28 | 'jest-watch-typeahead/testname' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/icons/your-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/public/icons/your-icon.png -------------------------------------------------------------------------------- /web/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /web/public/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/public/static/img/logo.png -------------------------------------------------------------------------------- /web/public/tinymce/skins/content/default/content.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | body { 8 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 9 | line-height: 1.4; 10 | margin: 1rem; 11 | } 12 | table { 13 | border-collapse: collapse; 14 | } 15 | table th, 16 | table td { 17 | border: 1px solid #ccc; 18 | padding: .4rem; 19 | } 20 | blockquote { 21 | border-left: 2px solid #ccc; 22 | margin-left: 1.5rem; 23 | padding-left: 1rem; 24 | } 25 | figure { 26 | display: table; 27 | margin: 1rem auto; 28 | } 29 | figure figcaption { 30 | color: #999; 31 | display: block; 32 | margin-top: .25rem; 33 | text-align: center; 34 | } 35 | hr { 36 | border-color: #ccc; 37 | border-style: solid; 38 | border-width: 1px 0 0 0; 39 | } 40 | code { 41 | background-color: #e8e8e8; 42 | border-radius: 3px; 43 | padding: .1rem .2rem; 44 | } -------------------------------------------------------------------------------- /web/public/tinymce/skins/content/default/content.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table td,table th{border:1px solid #ccc;padding:.4rem}blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem} 8 | /*# sourceMappingURL=content.min.css.map */ 9 | -------------------------------------------------------------------------------- /web/public/tinymce/skins/content/document/content.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | @media screen { 8 | html { 9 | background: #f4f4f4; 10 | } 11 | } 12 | body { 13 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 14 | } 15 | @media screen { 16 | body { 17 | background-color: #fff; 18 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.15); 19 | box-sizing: border-box; 20 | margin: 1rem auto 0; 21 | max-width: 820px; 22 | min-height: calc(99vh); 23 | padding: 4rem 6rem 6rem 6rem; 24 | } 25 | } 26 | table { 27 | border-collapse: collapse; 28 | } 29 | table th, 30 | table td { 31 | border: 1px solid #ccc; 32 | padding: .4rem; 33 | } 34 | blockquote { 35 | border-left: 2px solid #ccc; 36 | margin-left: 1.5rem; 37 | padding-left: 1rem; 38 | } 39 | figure figcaption { 40 | color: #999; 41 | margin-top: .25rem; 42 | text-align: center; 43 | } 44 | hr { 45 | border-color: #ccc; 46 | border-style: solid; 47 | border-width: 1px 0 0 0; 48 | } -------------------------------------------------------------------------------- /web/public/tinymce/skins/content/document/content.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | @media screen{html{background:#f4f4f4}}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif}@media screen{body{background-color:#fff;box-shadow:0 0 4px rgba(0,0,0,.15);box-sizing:border-box;margin:1rem auto 0;max-width:820px;min-height:calc(99vh);padding:4rem 6rem 6rem 6rem}}table{border-collapse:collapse}table td,table th{border:1px solid #ccc;padding:.4rem}blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}figure figcaption{color:#999;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0} 8 | /*# sourceMappingURL=content.min.css.map */ 9 | -------------------------------------------------------------------------------- /web/public/tinymce/skins/content/writer/content.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | body { 8 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 9 | line-height: 1.4; 10 | margin: 1rem auto; 11 | max-width: 900px; 12 | } 13 | table { 14 | border-collapse: collapse; 15 | } 16 | table th, 17 | table td { 18 | border: 1px solid #ccc; 19 | padding: .4rem; 20 | } 21 | blockquote { 22 | border-left: 2px solid #ccc; 23 | margin-left: 1.5rem; 24 | padding-left: 1rem; 25 | } 26 | figure { 27 | display: table; 28 | margin: 1rem auto; 29 | } 30 | figure figcaption { 31 | color: #999; 32 | display: block; 33 | margin-top: .25rem; 34 | text-align: center; 35 | } 36 | hr { 37 | border-color: #ccc; 38 | border-style: solid; 39 | border-width: 1px 0 0 0; 40 | } 41 | code { 42 | background-color: #e8e8e8; 43 | border-radius: 3px; 44 | padding: .1rem .2rem; 45 | } -------------------------------------------------------------------------------- /web/public/tinymce/skins/content/writer/content.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem auto;max-width:900px}table{border-collapse:collapse}table td,table th{border:1px solid #ccc;padding:.4rem}blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem} 8 | /*# sourceMappingURL=content.min.css.map */ 9 | -------------------------------------------------------------------------------- /web/public/tinymce/skins/ui/oxide/content.mobile.min.css: -------------------------------------------------------------------------------- 1 | .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position:absolute;display:inline-block;background-color:green;opacity:.5}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%} -------------------------------------------------------------------------------- /web/public/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/public/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff -------------------------------------------------------------------------------- /web/script/lib/exec-promise.js: -------------------------------------------------------------------------------- 1 | const child_process = require('child_process') 2 | 3 | // 执行命令 4 | function exec(cmd) { 5 | return new Promise((resolve, reject) => { 6 | child_process.exec(cmd, (error, stdout) => { 7 | if (error) { 8 | reject(error) 9 | } 10 | resolve(stdout) 11 | }) 12 | }) 13 | } 14 | 15 | module.exports = exec 16 | -------------------------------------------------------------------------------- /web/script/lib/semver-validate.js: -------------------------------------------------------------------------------- 1 | // 预计算一下版本是否有冲突 2 | const semver = require('semver') 3 | 4 | const validateSemver = (range1, range2) => { 5 | if (!range1 || !range2) { 6 | return false 7 | } 8 | // 都是指定版本 9 | if (semver.valid(range1) && semver.valid(range2)) { 10 | return (semver.coerce(range1) === semver.coerce(range2)) 11 | } 12 | 13 | // 都是范围 14 | if (semver.validRange(range1) && semver.validRange(range2)) { 15 | return semver.intersects(range1, range2) 16 | } 17 | 18 | // 一个版本一个范围 19 | if (semver.valid(range1) && semver.validRange(range2)) { 20 | return semver.satisfies(range1, range2) 21 | } 22 | 23 | if (semver.valid(range2) && semver.validRange(range1)) { 24 | return semver.satisfies(range2, range1) 25 | } 26 | 27 | return false 28 | } 29 | 30 | module.exports = validateSemver 31 | -------------------------------------------------------------------------------- /web/script/lib/util.js: -------------------------------------------------------------------------------- 1 | const came = str => `${str}`.replace(/-\D/g, match => match.charAt(1).toUpperCase()) 2 | 3 | const hyphenate = str => `${str}`.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`) 4 | 5 | module.exports = { 6 | came, 7 | hyphenate, 8 | } 9 | -------------------------------------------------------------------------------- /web/script/plugin-get-config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | // eslint-disable-next-line import/no-extraneous-dependencies 3 | const path = require('path') 4 | const chalk = require('chalk') 5 | const ejs = require('ejs') 6 | const getAllPlugin = require('./lib/plugin-get-all') 7 | 8 | const targetDir = path.resolve(__dirname, '../src/config/stage/plugin.js') 9 | const pluginsPath = path.resolve(__dirname, '../src/plugin') 10 | const templatePath = path.resolve(__dirname, './template/plugin-stage-config.js.ejs') 11 | 12 | // eslint-disable-next-line 13 | console.log(chalk.green('配置插件...')) 14 | 15 | const template = fs.readFileSync(templatePath, 'utf8') 16 | const puginList = getAllPlugin(pluginsPath) 17 | const result = ejs.render(template, { plugins: puginList }) 18 | 19 | fs.writeFile(targetDir, result) 20 | 21 | // eslint-disable-next-line 22 | console.log(chalk.green(`插件配置完成: ${targetDir}\n`)) 23 | -------------------------------------------------------------------------------- /web/script/template/plugin-stage-config.js.ejs: -------------------------------------------------------------------------------- 1 | // 本文件是自动生成, 请勿修改 2 | <% plugins.forEach(function(plugin){ %>import <%= plugin.camelCaseName %> from '@/plugin/<%= plugin.name %>/stage-config' 3 | <% }); %> 4 | const pluginsConfig = [ 5 | <% plugins.forEach(function(plugin){ %> <%= plugin.camelCaseName %>, 6 | <% }); %>] 7 | 8 | export default pluginsConfig 9 | -------------------------------------------------------------------------------- /web/script/template/plugin/README.md.ejs: -------------------------------------------------------------------------------- 1 | # 插件名: <%= title %>(<%= name %>) 2 | 3 | 插件描述, <%= title %> 用于处于xxx业务场景, 提供了xxx功能 4 | 5 | ## 舞台视口列表 6 | 7 | ### TestView 8 | 9 | 地址: `/<%= name %>/test.vue` 10 | 显示xxx内容, 可进行xxx操作 11 | 12 | ## 自由视口列表 13 | 14 | ### Test 15 | 16 | 属性: 17 | 18 | | Require | Name | Type | Default | Desc | 19 | |:-------:|:----:|:------:|:-------:|:----:| 20 | | true | name | String | val | 描述 | 21 | 22 | 事件: 23 | 24 | | Name | Argument | Desc | 25 | |:----:|:------------:|:----:| 26 | | name | 事件参数描述 | 描述 | 27 | 28 | 方法: 29 | 30 | | Name | Argument | Result | Desc | 31 | |:----:|:------------:|:----------:|:----:| 32 | | name | 方法参数描述 | 返回值描述 | 描述 | -------------------------------------------------------------------------------- /web/script/template/plugin/asset/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/script/template/plugin/asset/image/logo.png -------------------------------------------------------------------------------- /web/script/template/plugin/component/component.vue.ejs: -------------------------------------------------------------------------------- 1 | 7 | 8 | 39 | 40 | 42 | -------------------------------------------------------------------------------- /web/script/template/plugin/package.json.ejs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lc-plugin-<%= name %>", 3 | "title": "<%= title %>", 4 | "version": "<%= version %>", 5 | "_linVersion": "0.3.5", 6 | "description": "<%= description %>", 7 | "author": "<%= author %>", 8 | "dependencies": {}, 9 | "devDependencies": {} 10 | } -------------------------------------------------------------------------------- /web/script/template/plugin/stage-config.js.ejs: -------------------------------------------------------------------------------- 1 | const <%= camelCaseName %>Router = { 2 | route: null, 3 | name: null, 4 | title: '<%= title %>', 5 | type: 'folder', 6 | icon: 'iconfont icon-demo', 7 | filePath: 'views/<%= name %>/', 8 | order: null, 9 | inNav: true, 10 | children: [ 11 | { 12 | title: '舞台页面', 13 | type: 'view', 14 | name: '<%= name %>Stage1', 15 | route: '/<%= name %>/stage1', 16 | filePath: 'plugin/<%= name %>/view/stage1.vue', 17 | inNav: true, 18 | icon: 'iconfont icon-demo', 19 | right: null, 20 | }, 21 | { 22 | title: '舞台页面', 23 | type: 'view', 24 | name: '<%= name %>Stage2', 25 | route: '/<%= name %>/stage2', 26 | filePath: 'plugin/<%= name %>/view/stage2.vue', 27 | inNav: true, 28 | icon: 'iconfont icon-demo', 29 | right: null, 30 | }, 31 | ], 32 | } 33 | 34 | export default <%= camelCaseName %>Router 35 | -------------------------------------------------------------------------------- /web/script/template/plugin/view/stage1.vue.ejs: -------------------------------------------------------------------------------- 1 | 10 | 11 | 48 | 49 | 51 | -------------------------------------------------------------------------------- /web/script/template/plugin/view/stage2.vue.ejs: -------------------------------------------------------------------------------- 1 | 10 | 11 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /web/src/app.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | 28 | 57 | -------------------------------------------------------------------------------- /web/src/assets/image/error-page/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/error-page/404.png -------------------------------------------------------------------------------- /web/src/assets/image/error-page/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/error-page/logo.png -------------------------------------------------------------------------------- /web/src/assets/image/login/login-ba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/login/login-ba.png -------------------------------------------------------------------------------- /web/src/assets/image/login/login-btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/login/login-btn.png -------------------------------------------------------------------------------- /web/src/assets/image/login/nickname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/login/nickname.png -------------------------------------------------------------------------------- /web/src/assets/image/login/password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/login/password.png -------------------------------------------------------------------------------- /web/src/assets/image/login/team-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/login/team-name.png -------------------------------------------------------------------------------- /web/src/assets/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/logo.png -------------------------------------------------------------------------------- /web/src/assets/image/mobile-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/mobile-logo.png -------------------------------------------------------------------------------- /web/src/assets/image/user/corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/user/corner.png -------------------------------------------------------------------------------- /web/src/assets/image/user/user-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/user/user-bg.png -------------------------------------------------------------------------------- /web/src/assets/image/user/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/user/user.jpg -------------------------------------------------------------------------------- /web/src/assets/image/user/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/image/user/user.png -------------------------------------------------------------------------------- /web/src/assets/style/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/style/fonts/element-icons.ttf -------------------------------------------------------------------------------- /web/src/assets/style/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/assets/style/fonts/element-icons.woff -------------------------------------------------------------------------------- /web/src/assets/style/index.scss: -------------------------------------------------------------------------------- 1 | @import './realize/reset'; 2 | @import './realize/animation'; 3 | @import './realize/transition'; 4 | 5 | .lin-container { 6 | .el-divider--horizontal { 7 | margin:0px; 8 | } 9 | .lin-title { 10 | height: 59px; 11 | line-height: 59px; 12 | color: $parent-title-color; 13 | font-size: 16px; 14 | font-weight: 500; 15 | text-indent: 40px; 16 | border-bottom: 1px solid #dae1ed; 17 | } 18 | 19 | .lin-wrap { 20 | padding: 20px; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/src/assets/style/realize/animation.scss: -------------------------------------------------------------------------------- 1 | @keyframes fade { 2 | from { 3 | opacity: 0.1; 4 | } 5 | to { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | @keyframes img-add-view { 11 | 0% { 12 | opacity: 0; 13 | transform: scale(0.8); 14 | } 15 | 100% { 16 | opacity: 1; 17 | transform: scale(1); 18 | } 19 | } 20 | 21 | // 添加单品 22 | @keyframes arr-item-add { 23 | 0% { 24 | opacity: 0; 25 | transform: translateY(-100%); 26 | } 27 | 100% { 28 | opacity: 1; 29 | transform: translateY(0%); 30 | } 31 | } 32 | 33 | // 删除单品 34 | @keyframes arr-item-reduce { 35 | 0% { 36 | opacity: 1; 37 | transform: translateY(0%); 38 | } 39 | 100% { 40 | opacity: 0; 41 | transform: translateY(100%); 42 | } 43 | } 44 | 45 | // 页面切换动画 46 | .fade-transform-leave-active, 47 | .fade-transform-enter-active { 48 | transition: all 0.3s; 49 | } 50 | .fade-transform-enter { 51 | opacity: 0; 52 | transform: translateX(-30px); 53 | } 54 | .fade-transform-leave-to { 55 | opacity: 0; 56 | transform: translateX(30px); 57 | } 58 | 59 | .fadeChild-transform-leave-active, 60 | .fadeChild-transform-enter-active { 61 | transition: all 0.3s; 62 | } 63 | .fadeChild-transform-enter { 64 | opacity: 0; 65 | transform: translateX(-30px); 66 | } 67 | .fadeChild-transform-leave-to { 68 | opacity: 0; 69 | transform: translateX(30px); 70 | } 71 | -------------------------------------------------------------------------------- /web/src/assets/style/realize/transition.scss: -------------------------------------------------------------------------------- 1 | 2 | .fade-enter-active, .fade-leave-active { 3 | transition: opacity .5s; 4 | } 5 | .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { 6 | opacity: 0; 7 | } 8 | -------------------------------------------------------------------------------- /web/src/assets/style/realize/variable.scss: -------------------------------------------------------------------------------- 1 | $theme: #3963bc; 2 | 3 | /* 布局 */ 4 | $sidebar-width: 210px; 5 | $sidebar-background: #192a5e; 6 | 7 | $navbar-height: 30px; 8 | $navbar-padding: 20px; 9 | $header-height: 86px; 10 | $reuse-tab-height: 40px; 11 | 12 | $navbar-background: #BECCD8; 13 | $app-main-background: #F9FAFB; 14 | $header-background: #EEF4F9; 15 | $right-side-font-color: #666666; 16 | $reuse-tab-item-background: #FFFFFF; 17 | 18 | $title-color: #45526b; 19 | $parent-title-color: #3963bc; 20 | $table-border-color: #dee2e6; 21 | 22 | /* 菜单 */ 23 | $menu-item-hover: #0a1949; 24 | $menu-item-bg: #122150; 25 | $sub-menu-title: #c4c9d2; 26 | $menu-item-height: 50px; 27 | -------------------------------------------------------------------------------- /web/src/assets/style/shared.scss: -------------------------------------------------------------------------------- 1 | @import './realize/mixin.scss'; 2 | @import './realize/variable.scss'; -------------------------------------------------------------------------------- /web/src/component/base/dialog/lin-dialog.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/src/component/base/dropdown/lin-dropdown.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 33 | 34 | 40 | -------------------------------------------------------------------------------- /web/src/component/base/icon/lin-icon.vue: -------------------------------------------------------------------------------- 1 | 6 | 29 | 37 | -------------------------------------------------------------------------------- /web/src/component/base/search/lin-search.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 45 | 60 | -------------------------------------------------------------------------------- /web/src/component/base/source-code/source-code.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 49 | -------------------------------------------------------------------------------- /web/src/component/base/sticky-top/sticky-top.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /web/src/component/layout/app-main.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | 34 | -------------------------------------------------------------------------------- /web/src/component/layout/breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | 27 | 62 | -------------------------------------------------------------------------------- /web/src/component/layout/clear-tab.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | 29 | -------------------------------------------------------------------------------- /web/src/component/layout/index.js: -------------------------------------------------------------------------------- 1 | import NavBar from './nav-bar' 2 | import Sidebar from './sidebar/sidebar.vue' 3 | import AppMain from './app-main' 4 | import ReuseTab from './reuse-tab' 5 | import MenuTab from './menu-tab.vue' 6 | import BackTop from './back-top.vue' 7 | import User from './user.vue' 8 | 9 | export { NavBar, Sidebar, AppMain, ReuseTab, MenuTab, BackTop, User } 10 | -------------------------------------------------------------------------------- /web/src/component/layout/sidebar/logo.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | 27 | 64 | -------------------------------------------------------------------------------- /web/src/component/notify/emitter.js: -------------------------------------------------------------------------------- 1 | class Emitter { 2 | constructor() { 3 | this.listeners = new Map() 4 | } 5 | 6 | addListener(label, callback, vm) { 7 | if (typeof callback === 'function') { 8 | // eslint-disable-next-line no-unused-expressions 9 | this.listeners.has(label) || this.listeners.set(label, []) 10 | this.listeners.get(label).push({ callback, vm }) 11 | return true 12 | } 13 | return false 14 | } 15 | 16 | removeListener(label, callback, vm) { 17 | const listeners = this.listeners.get(label) 18 | let index 19 | 20 | if (listeners?.length) { 21 | index = listeners.reduce((i, listener, index) => { 22 | if (typeof listener.callback === 'function' && listener.callback === callback && listener.vm === vm) { 23 | // eslint-disable-next-line no-param-reassign 24 | i = index 25 | } 26 | return i 27 | }, -1) 28 | 29 | if (index > -1) { 30 | listeners.splice(index, 1) 31 | this.listeners.set(label, listeners) 32 | return true 33 | } 34 | } 35 | return false 36 | } 37 | 38 | emit(label, ...args) { 39 | const listeners = this.listeners.get(label) 40 | 41 | if (listeners?.length) { 42 | listeners.forEach(listener => { 43 | listener.callback.call(listener.vm, ...args) 44 | }) 45 | return true 46 | } 47 | return false 48 | } 49 | } 50 | 51 | export default new Emitter() 52 | -------------------------------------------------------------------------------- /web/src/config/echarts.js: -------------------------------------------------------------------------------- 1 | import * as echarts from "echarts/core" 2 | import { LineChart } from "echarts/charts" 3 | import { 4 | TitleComponent, 5 | TooltipComponent, 6 | GridComponent, 7 | DatasetComponent, 8 | TransformComponent, 9 | LegendComponent, 10 | ToolboxComponent, 11 | } from "echarts/components" 12 | import { LabelLayout, UniversalTransition } from "echarts/features" 13 | import { CanvasRenderer } from "echarts/renderers" 14 | 15 | echarts.use([ 16 | TitleComponent, 17 | TooltipComponent, 18 | GridComponent, 19 | DatasetComponent, 20 | TransformComponent, 21 | LegendComponent, 22 | ToolboxComponent, 23 | LabelLayout, 24 | UniversalTransition, 25 | CanvasRenderer, 26 | LineChart, 27 | ]) 28 | 29 | export default echarts 30 | -------------------------------------------------------------------------------- /web/src/config/error-code.js: -------------------------------------------------------------------------------- 1 | const errorCode = { 2 | 777: '前端错误码未定义', 3 | 999: '服务器未知错误', 4 | 10000: '未携带令牌', 5 | 10020: '资源不存在', 6 | 10030: '参数错误', 7 | 10041: 'assessToken损坏', 8 | 10042: 'refreshToken损坏', 9 | 10051: 'assessToken过期', 10 | 10052: 'refreshToken过期', 11 | 10060: '字段重复', 12 | 10070: '不可操作', 13 | } 14 | 15 | export default errorCode 16 | -------------------------------------------------------------------------------- /web/src/config/global.js: -------------------------------------------------------------------------------- 1 | window.MAX_SUCCESS_CODE = 9998 2 | -------------------------------------------------------------------------------- /web/src/config/index.js: -------------------------------------------------------------------------------- 1 | const Config = { 2 | sidebarLevel: 3, // 侧边栏层级(无限级)限制, 默认为 3 级,可根据需求自行扩充 3 | openAutoJumpOut: true, // 是否开启无操作跳出 4 | defaultRoute: '/about', // 默认打开的路由 5 | websocketEnable: false, // 默认关闭 websocket 6 | showSidebarSearch: true, // 默认打开侧边栏搜索 7 | notLoginRoute: ['login'], // 无需登录即可访问的路由 name, 8 | useFrontEndErrorMsg: false, // 默认采用后端返回异常 9 | stagnateTime: 60 * 60 * 1000, // 无操作停滞时间,默认1小时 10 | baseURL: process.env.VUE_APP_BASE_URL, // API接口baseURL,在根目录.env文件查找对应环境变量配置 11 | } 12 | 13 | export default Config 14 | -------------------------------------------------------------------------------- /web/src/config/stage/book.js: -------------------------------------------------------------------------------- 1 | const bookRouter = { 2 | route: null, 3 | name: null, 4 | title: '图书管理', 5 | type: 'folder', // 类型: folder, tab, view 6 | icon: 'iconfont icon-tushuguanli', 7 | filePath: 'view/book/', // 文件路径 8 | order: null, 9 | inNav: true, 10 | permission: ['图书管理'], 11 | children: [ 12 | { 13 | title: '图书列表', 14 | type: 'view', 15 | name: 'BookCreate', 16 | route: '/book/list', 17 | filePath: 'view/book/book-list.vue', 18 | inNav: true, 19 | icon: 'iconfont icon-tushuguanli', 20 | }, 21 | { 22 | title: '添加图书', 23 | type: 'view', 24 | name: 'BookCreate', 25 | route: '/book/add', 26 | filePath: 'view/book/book.vue', 27 | inNav: true, 28 | icon: 'iconfont icon-add', 29 | }, 30 | ], 31 | } 32 | 33 | export default bookRouter 34 | -------------------------------------------------------------------------------- /web/src/config/stage/case.js: -------------------------------------------------------------------------------- 1 | const caseRouter = { 2 | route: '/case/list', 3 | name: 'case', 4 | title: '用例管理', 5 | type: 'view', // 类型: folder, tab, view 6 | icon: 'iconfont icon-caseStore', 7 | filePath: 'view/case/case-list.vue', // 文件路径 8 | order: 2, 9 | inNav: true, 10 | permission: ['用例管理'], 11 | keepAlive: true, 12 | } 13 | 14 | export default caseRouter 15 | -------------------------------------------------------------------------------- /web/src/config/stage/center.js: -------------------------------------------------------------------------------- 1 | const centerRouter = { 2 | route: null, 3 | name: null, 4 | title: '个人', 5 | type: 'view', // 类型: folder, tab, view 6 | icon: 'iconfont icon-tushuguanli', 7 | filePath: 'view/center/', // 文件路径 8 | order: null, 9 | inNav: false, 10 | } 11 | 12 | export default centerRouter 13 | -------------------------------------------------------------------------------- /web/src/config/stage/hidden.js: -------------------------------------------------------------------------------- 1 | const hiddenRouter = { 2 | route: null, 3 | name: 'hidden', 4 | title: '', 5 | type: 'folder', // 类型: folder, tab, view 6 | icon: 'iconfont icon-iconset0103', 7 | filePath: 'view/hidden/', // 文件路径 8 | order: null, 9 | inNav: false, 10 | children: [ 11 | { 12 | title: '测试详情', 13 | type: 'view', 14 | name: 'caseDetail', 15 | route: '/case/detail', 16 | filePath: 'view/case/detail.vue', 17 | inNav: false, 18 | icon: 'iconfont icon-iconset0103', 19 | keepAlive: true, 20 | }, 21 | ], 22 | } 23 | 24 | export default hiddenRouter 25 | -------------------------------------------------------------------------------- /web/src/config/stage/jmeter.js: -------------------------------------------------------------------------------- 1 | const jmeterRouter = { 2 | route: null, 3 | name: null, 4 | title: '聚合报告记录', 5 | type: 'tab', // 类型: folder, tab, view 6 | icon: 'iconfont icon-aggregateReport', 7 | filePath: 'view/jmeter/', // 文件路径 8 | order: null, 9 | inNav: true, 10 | permission: ['聚合报告记录'], 11 | children: [ 12 | { 13 | title: '查询', 14 | type: 'view', 15 | name: 'aggregateReportSearch', 16 | route: '/jmeter/aggregateReport/search', 17 | filePath: 'view/jmeter/aggregate-report-search.vue', 18 | inNav: true, 19 | icon: 'iconfont icon-search', 20 | keepAlive: true, 21 | }, 22 | { 23 | title: '记录', 24 | type: 'view', 25 | name: 'aggregateReportRecord', 26 | route: '/jmeter/aggregateReport/record', 27 | filePath: 'view/jmeter/aggregate-report-record.vue', 28 | inNav: true, 29 | icon: 'iconfont icon-record', 30 | keepAlive: true, 31 | }, 32 | ], 33 | } 34 | 35 | export default jmeterRouter 36 | -------------------------------------------------------------------------------- /web/src/config/stage/machine.js: -------------------------------------------------------------------------------- 1 | const machineRouter = { 2 | route: '/machine/list', 3 | name: 'machine', 4 | title: '压力机管理', 5 | type: 'view', // 类型: folder, tab, view 6 | icon: 'iconfont icon-machine', 7 | filePath: 'view/machine/machine-list.vue', // 文件路径 8 | order: 5, 9 | inNav: true, 10 | permission: ['压力机管理'], 11 | } 12 | 13 | export default machineRouter 14 | -------------------------------------------------------------------------------- /web/src/config/stage/plugin.js: -------------------------------------------------------------------------------- 1 | // 本文件是自动生成, 请勿修改 2 | import custom from '@/plugin/custom/stage-config' 3 | 4 | const pluginsConfig = [ 5 | custom, 6 | ] 7 | 8 | export default pluginsConfig 9 | -------------------------------------------------------------------------------- /web/src/config/stage/project.js: -------------------------------------------------------------------------------- 1 | const projectRouter = { 2 | route: '/project/list', 3 | name: 'project', 4 | title: '项目管理', 5 | type: 'view', // 类型: folder, tab, view 6 | icon: 'iconfont icon-project', 7 | filePath: 'view/project/project-list.vue', // 文件路径 8 | order: 4, 9 | inNav: true, 10 | permission: ['项目管理'], 11 | } 12 | 13 | export default projectRouter 14 | -------------------------------------------------------------------------------- /web/src/config/stage/task.js: -------------------------------------------------------------------------------- 1 | const taskRouter = { 2 | route: '/task/history', 3 | name: 'case', 4 | title: '测试记录', 5 | type: 'view', // 类型: folder, tab, view 6 | icon: 'iconfont icon-history', 7 | filePath: 'view/task/history-list.vue', // 文件路径 8 | order: 3, 9 | inNav: true, 10 | permission: ['测试记录'], 11 | keepAlive: true, 12 | } 13 | 14 | export default taskRouter -------------------------------------------------------------------------------- /web/src/lin/context/admin.js: -------------------------------------------------------------------------------- 1 | import { provide, ref, inject } from 'vue' 2 | 3 | // eslint-disable-next-line symbol-description 4 | const adminSymbol = Symbol() 5 | 6 | export const useAdminProvide = () => { 7 | const flag = ref(false) 8 | provide(adminSymbol, flag) 9 | } 10 | 11 | export const useAdminInject = () => { 12 | const adminContext = inject(adminSymbol) 13 | 14 | return adminContext 15 | } 16 | -------------------------------------------------------------------------------- /web/src/lin/context/index.js: -------------------------------------------------------------------------------- 1 | import { useAdminProvide, useAdminInject } from './admin' 2 | 3 | export { useAdminInject } 4 | 5 | export const useProvide = () => { 6 | useAdminProvide() 7 | } 8 | -------------------------------------------------------------------------------- /web/src/lin/directive/authorize.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | /** 4 | * 判断是否允许访问该DOM 5 | * @param {*} permission 权限 6 | * @param {*} user 当前用户实例 7 | * @param {*} permissions 当前管理员所在分组权限集 8 | */ 9 | function isAllowed(permission, user, permissions) { 10 | if (user.admin) return true 11 | 12 | if (typeof permission === 'string') { 13 | return permissions.includes(permission) 14 | } 15 | if (permission instanceof Array) { 16 | return permission.some(auth => permissions.indexOf(auth) >= 0) 17 | } 18 | return false 19 | } 20 | 21 | export default { 22 | beforeMount(el, binding) { 23 | let type 24 | let permission 25 | const element = el 26 | 27 | if (Object.prototype.toString.call(binding.value) === '[object Object]') { 28 | ;({ permission } = binding.value); 29 | ({ type } = binding.value) 30 | } else { 31 | permission = binding.value 32 | } 33 | const isAllow = isAllowed(permission, store.state.user || {}, store.state.permissions) 34 | if (!isAllow && permission) { 35 | if (type) { 36 | element.disabled = true 37 | element.style.opacity = 0.4 38 | element.style.cursor = 'not-allowed' 39 | } else { 40 | element.style.display = 'none' 41 | } 42 | } 43 | }, 44 | } 45 | -------------------------------------------------------------------------------- /web/src/lin/directive/index.js: -------------------------------------------------------------------------------- 1 | import './authorize' 2 | -------------------------------------------------------------------------------- /web/src/lin/model/notify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | import { post, get, put } from '@/lin/plugin/axios' 3 | import Config from '../../config' 4 | import Sse from '../util/sse' 5 | 6 | export default class Notify { 7 | url = null 8 | 9 | events = null 10 | 11 | sse = null 12 | 13 | constructor(url) { 14 | this.url = url 15 | } 16 | 17 | async getEvents() { 18 | const res = await get('cms/notify/events') 19 | this.events = res.events 20 | } 21 | 22 | async initSse() { 23 | await this.getEvents() 24 | this.sse = new Sse(Config.baseUrl + this.url, this.events) 25 | } 26 | 27 | /** 28 | * 创建events 29 | * @param {number} group_id 30 | * @param {Array} events 31 | */ 32 | // eslint-disable-next-line camelcase 33 | async createEvents(group_id, events) { 34 | const res = await post('cms/notify/events', { group_id, events }) 35 | return res 36 | } 37 | 38 | /** 39 | * 更新events 40 | * @param {number} group_id 41 | * @param {Array} events 42 | */ 43 | // eslint-disable-next-line camelcase 44 | async updateEvents(group_id, events) { 45 | const res = await put('cms/notify/events', { group_id, events }) 46 | return res 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web/src/lin/plugin/index.js: -------------------------------------------------------------------------------- 1 | import './axios' 2 | -------------------------------------------------------------------------------- /web/src/lin/util/auto-jump.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 定时自动登出功能, 启用后一段时间无用户操作, 则自动登出. 需在项目 config 中配置 3 | */ 4 | import store from '@/store' 5 | import Config from '@/config' 6 | 7 | 8 | let timer 9 | 10 | export default router => { 11 | if (timer) clearTimeout(timer) 12 | if (!Config.openAutoJumpOut) return 13 | if (router?.currentRoute.value.path === '/' || router?.currentRoute.value.path === '/login') { 14 | return 15 | } 16 | 17 | timer = setTimeout(() => { 18 | store.dispatch('loginOut') 19 | const { origin } = window.location 20 | window.location.href = origin 21 | }, Config.stagnateTime) 22 | } 23 | -------------------------------------------------------------------------------- /web/src/lin/util/cookie.js: -------------------------------------------------------------------------------- 1 | import cookies from 'js-cookie' 2 | /** 3 | * 存储tokens 4 | * @param {string} accessToken 5 | * @param {string} refreshToken 6 | */ 7 | export function saveTokens(accessToken, refreshToken) { 8 | // 存储tokens tokens只进入cookies,不进入vuex全局管理 9 | cookies.set('access_token', `Bearer ${accessToken}`) 10 | cookies.set('refresh_token', `Bearer ${refreshToken}`) 11 | } 12 | 13 | /** 14 | * 存储access_token 15 | * @param {string} accessToken 16 | */ 17 | export function saveAccessToken(accessToken) { 18 | cookies.set('access_token', `Bearer ${accessToken}`) 19 | } 20 | 21 | /** 22 | * 获得某个token 23 | * @param {string} tokenKey 24 | */ 25 | export function getToken(tokenKey) { 26 | return cookies.get(tokenKey) 27 | } 28 | 29 | /** 30 | * 移除token 31 | */ 32 | export function removeToken() { 33 | cookies.remove('access_token') 34 | cookies.remove('refresh_token') 35 | sessionStorage.removeItem('flag') 36 | sessionStorage.clear() 37 | localStorage.clear() 38 | } 39 | -------------------------------------------------------------------------------- /web/src/lin/util/date.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | 3 | // 设置语言为中文 4 | moment.locale('zh-cn') 5 | 6 | /** 7 | * @param {number} hours 8 | */ 9 | export function getDateAfterHours(hours) { 10 | const now = new Date() 11 | return new Date(now.setHours(now.getHours() + hours)) 12 | } 13 | /** 14 | * @param {number} days 15 | */ 16 | export function getDateAfterDays(days) { 17 | const now = new Date() 18 | return new Date(now.setHours(now.getHours() + days * 24)) 19 | } 20 | -------------------------------------------------------------------------------- /web/src/lin/util/emitter.js: -------------------------------------------------------------------------------- 1 | import mitt from 'mitt' 2 | 3 | export default mitt() 4 | -------------------------------------------------------------------------------- /web/src/lin/util/index.js: -------------------------------------------------------------------------------- 1 | export * from './date' 2 | -------------------------------------------------------------------------------- /web/src/lin/util/search.js: -------------------------------------------------------------------------------- 1 | import FastScanner from 'fastscan' 2 | 3 | // const words = ['今日头条', 4 | // '微信', '支付宝', 5 | // ] 6 | // const scanner = new FastScanner(words) 7 | // const content = '今日头条小程序终于来了,这是继微信、支付宝、百度后,第四个推出小程序功能的App。猫眼电影率先试水,出现在今日头条。' 8 | // const offWords = scanner.search(content) 9 | // console.log(offWords) 10 | // const hits = scanner.hits(content) 11 | // console.log(hits) 12 | 13 | /** 14 | * 15 | * @param {string} word 16 | * @param {string} content 17 | */ 18 | export async function searchForWord(word, content) { 19 | const scanner = new FastScanner([word]) 20 | const offWords = scanner.search(content) 21 | return offWords 22 | } 23 | 24 | /** 25 | * 26 | * @param {Array} words 27 | * @param {string} content 28 | */ 29 | export async function searchForWords(words, content) { 30 | const scanner = new FastScanner(words) 31 | const offWords = scanner.search(content) 32 | return offWords 33 | } 34 | /** 35 | * 36 | * @param {string} keyword 37 | * @param {Array} logs 38 | */ 39 | export function searchLogKeyword(keyword, logs, className = 'strong') { 40 | console.log('keyword', keyword) 41 | console.log('logs', logs) 42 | const _logs = logs.map(log => { 43 | let msg = log.message 44 | msg = msg.replace(RegExp(`${keyword}`, 'g'), `${keyword}`) 45 | // eslint-disable-next-line 46 | log.message = msg 47 | return log 48 | }) 49 | return _logs 50 | } 51 | -------------------------------------------------------------------------------- /web/src/lin/util/sse.js: -------------------------------------------------------------------------------- 1 | import { ElMessage } from 'element-plus' 2 | 3 | // import EventSourcePolyfill from 'event-source-polyfill' 4 | import 'event-source-polyfill/src/eventsource' 5 | import { getToken } from './cookie' 6 | import store from '../../store' 7 | 8 | export default class Sse { 9 | source = null 10 | 11 | /** 12 | * 需在vuex中确认有user对象后才能初始化,否则不连接服务器 13 | * 注意: sse单独走自己的请求路线,不与axios重合,所以axios里面的配置在此处失效 14 | * @param {string} url sse全路径 15 | * @param {Array} events 当前用户可监听的路径 16 | */ 17 | constructor(url, events) { 18 | /* eslint-disable no-undef */ 19 | console.log(url, events) 20 | this.source = new EventSourcePolyfill(url, { 21 | headers: { 22 | Authorization: getToken('access_token'), 23 | }, 24 | }) 25 | this.open() 26 | 27 | events.forEach(event => { 28 | this.addEventListener(event) 29 | }) 30 | } 31 | 32 | open() { 33 | this.source.onopen = event => { 34 | console.log('sse opened', event) 35 | } 36 | } 37 | 38 | error() { 39 | this.source.onerror = event => { 40 | console.log('error', event) 41 | } 42 | } 43 | 44 | addEventListener(eventName) { 45 | this.source.addEventListener(eventName, event => { 46 | // console.log('receive one message: ', event.data) 47 | // console.log('receive one message: ', event.lastEventId) 48 | store.commit('MARK_UNREAD_MESSAGE', { data: event.data, id: event.lastEventId }) 49 | ElMessage.warning(JSON.parse(event.data).message) 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /web/src/lin/util/storage.js: -------------------------------------------------------------------------------- 1 | import storage from 'good-storage' 2 | 3 | const LOGIN_KEY = '__login__' 4 | 5 | export function setLoggedIn(flag) { 6 | storage.session.set(LOGIN_KEY, flag) 7 | return flag 8 | } 9 | 10 | export function loadLoggedIn() { 11 | return storage.session.get(LOGIN_KEY, '') 12 | } 13 | 14 | export function cleanLoggedIn() { 15 | storage.session.remove(LOGIN_KEY) 16 | } 17 | -------------------------------------------------------------------------------- /web/src/lin/util/token.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 存储tokens 3 | * @param {string} accessToken 4 | * @param {string} refreshToken 5 | */ 6 | export function saveTokens(accessToken, refreshToken) { 7 | localStorage.setItem('access_token', `Bearer ${accessToken}`) 8 | localStorage.setItem('refresh_token', `Bearer ${refreshToken}`) 9 | } 10 | 11 | /** 12 | * 存储access_token 13 | * @param {string} accessToken 14 | */ 15 | export function saveAccessToken(accessToken) { 16 | localStorage.setItem('access_token', `Bearer ${accessToken}`) 17 | } 18 | 19 | /** 20 | * 获得某个token 21 | * @param {string} tokenKey 22 | */ 23 | export function getToken(tokenKey) { 24 | return localStorage.getItem(tokenKey) 25 | } 26 | 27 | /** 28 | * 移除token 29 | */ 30 | export function removeToken() { 31 | localStorage.removeItem('access_token') 32 | localStorage.removeItem('refresh_token') 33 | } 34 | -------------------------------------------------------------------------------- /web/src/model/book.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | import _axios, { get, put, _delete } from '@/lin/plugin/axios' 3 | 4 | // 我们通过 class 这样的语法糖使模型这个概念更加具象化,其优点:耦合性低、可维护性。 5 | class Book { 6 | // constructor() {} 7 | 8 | // 类中的方法可以代表一个用户行为 9 | async createBook(data) { 10 | return _axios({ 11 | method: 'post', 12 | url: 'v1/book', 13 | data, 14 | }) 15 | } 16 | 17 | // 在这里通过 async await 语法糖让代码同步执行 18 | // 1. await 一定要搭配 async 来使用 19 | // 2. await 后面跟的是一个 Promise 对象 20 | async getBook(id) { 21 | const res = await get(`v1/book/${id}`) 22 | return res 23 | } 24 | 25 | async editBook(id, info) { 26 | const res = await put(`v1/book/${id}`, info) 27 | return res 28 | } 29 | 30 | async deleteBook(id) { 31 | const res = await _delete(`v1/book/${id}`) 32 | return res 33 | } 34 | 35 | async getBooks() { 36 | return _axios({ 37 | method: 'get', 38 | url: 'v1/book', 39 | handleError: true, 40 | }) 41 | } 42 | } 43 | 44 | export default new Book() 45 | -------------------------------------------------------------------------------- /web/src/plugin/custom/assets/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guojiaxing1995/easy-jmeter/892f8b58cfb870a2c69d848ce0fd21b87918da1b/web/src/plugin/custom/assets/image/logo.png -------------------------------------------------------------------------------- /web/src/plugin/custom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lc-plugin-custom", 3 | "title": "自定义组件展示", 4 | "version": "1.0.0", 5 | "_linVersion": "0.4.0", 6 | "description": "自定义组件展示", 7 | "author": "", 8 | "dependencies": { 9 | "element-plus": "^1.0.2-beta.36" 10 | }, 11 | "devDependencies": {} 12 | } 13 | -------------------------------------------------------------------------------- /web/src/plugin/custom/stage-config.js: -------------------------------------------------------------------------------- 1 | const CustomRouter = { 2 | route: null, 3 | name: null, 4 | title: '自定义组件', 5 | type: 'folder', 6 | icon: 'iconfont icon-zidingyi', 7 | filePath: 'view/custom/', 8 | order: null, 9 | inNav: true, 10 | permission: ['自定义组件'], 11 | children: [ 12 | { 13 | title: 'multiple 多重输入', 14 | type: 'view', 15 | name: 'Multiple', 16 | route: '/custom/multiple', 17 | filePath: 'plugin/custom/view/multiple-input.vue', 18 | inNav: true, 19 | icon: 'iconfont icon-multiple_inputs', 20 | permission: null, 21 | }, 22 | ], 23 | } 24 | 25 | export default CustomRouter 26 | -------------------------------------------------------------------------------- /web/src/router/home-router.js: -------------------------------------------------------------------------------- 1 | import stageConfig from '@/config/stage' // 引入舞台配置 2 | 3 | /** 4 | * 深度遍历配置树, 摘取叶子节点作为路由部分 5 | * @param {*} config 配置项 6 | * @param {*} fuc 回调函数 7 | */ 8 | function deepTravel(config, fuc) { 9 | if (Array.isArray(config)) { 10 | config.forEach(subConfig => { 11 | deepTravel(subConfig, fuc) 12 | }) 13 | } else if (config.children?.length) { 14 | config.children.forEach(subConfig => { 15 | deepTravel(subConfig, fuc) 16 | }) 17 | } else { 18 | fuc(config) 19 | } 20 | } 21 | 22 | const homeRouter = [] 23 | 24 | /** 25 | * 构造舞台view路由 26 | */ 27 | deepTravel(stageConfig, viewConfig => { 28 | const viewRouter = {} 29 | viewRouter.path = viewConfig.route 30 | viewRouter.name = viewConfig.name 31 | viewRouter.component = () => import(`@/${viewConfig.filePath}`) 32 | viewRouter.meta = { 33 | title: viewConfig.title, 34 | icon: viewConfig.icon, 35 | permission: viewConfig.permission, 36 | type: viewConfig.type, 37 | blueBaseColor: viewConfig.blueBaseColor ? 'viewConfig.blueBaseColor' : '', 38 | keepAlive: viewConfig.keepAlive, 39 | } 40 | homeRouter.push(viewRouter) 41 | }) 42 | 43 | export default homeRouter 44 | -------------------------------------------------------------------------------- /web/src/router/route.js: -------------------------------------------------------------------------------- 1 | import homeRouter from './home-router' 2 | 3 | const routes = [ 4 | { 5 | path: '/', 6 | name: 'Home', 7 | redirect: '/about', 8 | component: () => import('@/view/home/home'), 9 | children: [...homeRouter], 10 | }, 11 | { 12 | path: '/login', 13 | name: 'login', 14 | component: () => import('@/view/login/login'), 15 | }, 16 | { 17 | redirect: '/404', 18 | path: '/:pathMatch(.*)', 19 | }, 20 | ] 21 | 22 | export default routes 23 | -------------------------------------------------------------------------------- /web/src/store/action.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation-type' 2 | 3 | export default { 4 | setUserAndState({ commit }, user) { 5 | commit(types.SET_USER, user) 6 | commit(types.SET_LOGGED_IN, true) 7 | }, 8 | 9 | loginOut({ commit }) { 10 | localStorage.clear() 11 | commit(types.REMOVE_LOGGED_IN, false) 12 | }, 13 | 14 | readMessage({ commit }, message) { 15 | commit(types.REMOVE_UNREAD_MESSAGE, message.id) 16 | commit(types.MARK_READ_MESSAGE, message) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /web/src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, createLogger } from 'vuex' 2 | import VuexPersistence from 'vuex-persist' 3 | 4 | import mutations from './mutation' 5 | import state from './state' 6 | import * as getters from './getter' 7 | import actions from './action' 8 | 9 | const vuexLocal = new VuexPersistence({ 10 | storage: window.localStorage, 11 | reducer: stateData => ({ 12 | user: stateData.user, 13 | loggedIn: stateData.loggedIn, 14 | permissions: stateData.permissions, 15 | }), 16 | }) 17 | 18 | const debug = process.env.NODE_ENV !== 'production' 19 | 20 | export default createStore({ 21 | state, 22 | getters, 23 | actions, 24 | mutations, 25 | strict: debug, 26 | plugins: debug ? [vuexLocal.plugin, createLogger()] : [vuexLocal.plugin], 27 | }) 28 | -------------------------------------------------------------------------------- /web/src/store/mutation-type.js: -------------------------------------------------------------------------------- 1 | export const SET_LOGGED_IN = 'SET_LOGGED_IN' 2 | 3 | export const REMOVE_LOGGED_IN = 'REMOVE_LOGGED_IN' 4 | 5 | export const SET_USER = 'SET_USER' 6 | 7 | export const MARK_READ_MESSAGE = 'MARK_READ_MESSAGE' 8 | 9 | export const REMOVE_UNREAD_MESSAGE = 'REMOVE_UNREAD_MESSAGE' 10 | 11 | export const MARK_UNREAD_MESSAGE = 'MARK_UNREAD_MESSAGE' 12 | 13 | export const SET_USER_PERMISSIONS = 'SET_USER_PERMISSIONS' 14 | 15 | export const SET_REFRESH_OPTION = 'SET_REFRESH_OPTION' 16 | -------------------------------------------------------------------------------- /web/src/store/mutation.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation-type' 2 | 3 | export default { 4 | [types.SET_LOGGED_IN](state) { 5 | state.loggedIn = true 6 | }, 7 | 8 | [types.REMOVE_LOGGED_IN](state) { 9 | state.loggedIn = false 10 | state.user = null 11 | }, 12 | 13 | [types.SET_USER](state, payload) { 14 | state.user = payload 15 | }, 16 | 17 | [types.MARK_READ_MESSAGE](state, payload) { 18 | state.alreadyReadMessages.push(payload) 19 | }, 20 | 21 | [types.MARK_UNREAD_MESSAGE](state, payload) { 22 | // console.log('===: ', payload) 23 | state.unreadMessages.push(payload) 24 | }, 25 | 26 | [types.REMOVE_UNREAD_MESSAGE](state, payload) { 27 | // payload => message.id 28 | const { unreadMessages } = state 29 | const index = unreadMessages.findIndex(el => el.id === payload) 30 | unreadMessages.splice(index, 1) 31 | }, 32 | 33 | [types.SET_USER_PERMISSIONS](state, permissions) { 34 | state.permissions = permissions 35 | .map(permission => Object.values(permission)) 36 | .flat(2) 37 | .map(p => p.permission) 38 | }, 39 | 40 | [types.SET_REFRESH_OPTION](state, option) { 41 | state.refreshOptions = option 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /web/src/store/socket.js: -------------------------------------------------------------------------------- 1 | import io from 'socket.io-client' 2 | export default { 3 | install: (app, { connection, options }) => { 4 | var socket = io(connection, options) 5 | app.config.globalProperties.$socket = socket 6 | app.provide('socket', socket) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /web/src/store/state.js: -------------------------------------------------------------------------------- 1 | import appConfig from '@/config/index' // 引入项目配置 2 | import stageConfig from '@/config/stage' // 引入舞台配置 3 | 4 | export default { 5 | user: {}, // 当前用户 6 | loggedIn: false, // 是否登录 7 | permissions: [], // 每个用户的所有权限 8 | 9 | // 推送消息 10 | unreadMessages: [], 11 | alreadyReadMessages: [], 12 | 13 | // 舞台配置 14 | stageConfig, 15 | 16 | // 当前页信息 17 | currentRoute: { 18 | config: null, 19 | treePath: [], 20 | }, 21 | 22 | sidebarLevel: appConfig.sidebarLevel || 3, 23 | defaultRoute: appConfig.defaultRoute || '/about', 24 | } 25 | -------------------------------------------------------------------------------- /web/src/view/admin/user/user-create.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 39 | 40 | 57 | -------------------------------------------------------------------------------- /web/src/view/error-page/404.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 43 | -------------------------------------------------------------------------------- /web/tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } -------------------------------------------------------------------------------- /web/vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | function resolve(dir) { 4 | return path.join(__dirname, dir) 5 | } 6 | 7 | module.exports = { 8 | lintOnSave: false, 9 | productionSourceMap: false, 10 | // assetsDir: 'static', 11 | chainWebpack: config => { 12 | config.resolve.alias.set('@', resolve('src')).set('lin', resolve('src/lin')).set('assets', resolve('src/assets')) 13 | config.module.rule('ignore').test(/\.md$/).use('ignore-loader').loader('ignore-loader').end() 14 | }, 15 | configureWebpack: { 16 | devtool: 'source-map', 17 | resolve: { 18 | extensions: ['.js', '.json', '.vue', '.scss', '.html'], 19 | }, 20 | }, 21 | css: { 22 | loaderOptions: { 23 | sass: { 24 | prependData: `@import "@/assets/style/shared.scss";`, 25 | }, 26 | }, 27 | }, 28 | devServer: {}, 29 | // node_modules依赖项es6语法未转换问题 30 | transpileDependencies: ['vuex-persist'], 31 | } 32 | --------------------------------------------------------------------------------