├── .gitignore ├── LICENSE ├── README.md ├── backend ├── pom.xml └── src │ └── main │ ├── java │ └── cc │ │ └── mrbird │ │ └── febs │ │ ├── FebsApplication.java │ │ ├── common │ │ ├── annotation │ │ │ ├── DataFilter.java │ │ │ ├── IsCron.java │ │ │ ├── Limit.java │ │ │ ├── Log.java │ │ │ └── NotBlank.java │ │ ├── aspect │ │ │ ├── LimitAspect.java │ │ │ └── LogAspect.java │ │ ├── authentication │ │ │ ├── JWTFilter.java │ │ │ ├── JWTToken.java │ │ │ ├── JWTUtil.java │ │ │ ├── ShiroConfig.java │ │ │ └── ShiroRealm.java │ │ ├── config │ │ │ ├── AsyncExecutorPoolConfig.java │ │ │ ├── FebsConfig.java │ │ │ ├── MybatisPlusConfig.java │ │ │ ├── P6spySqlFormatConfig.java │ │ │ └── RedisConfig.java │ │ ├── controller │ │ │ └── BaseController.java │ │ ├── converter │ │ │ ├── DataScopeReadConverter.java │ │ │ ├── DataScopeWriteConverter.java │ │ │ └── TimeConverter.java │ │ ├── domain │ │ │ ├── ActiveUser.java │ │ │ ├── FebsConstant.java │ │ │ ├── FebsResponse.java │ │ │ ├── LimitType.java │ │ │ ├── QueryRequest.java │ │ │ ├── RedisInfo.java │ │ │ ├── RegexpConstant.java │ │ │ ├── Tree.java │ │ │ └── router │ │ │ │ ├── RouterMeta.java │ │ │ │ └── VueRouter.java │ │ ├── enums │ │ │ └── FilterType.java │ │ ├── exception │ │ │ ├── FebsException.java │ │ │ ├── LimitAccessException.java │ │ │ ├── RedisConnectException.java │ │ │ ├── TokenTimeoutException.java │ │ │ └── code │ │ │ │ └── Code.java │ │ ├── function │ │ │ ├── CacheSelector.java │ │ │ └── JedisExecutor.java │ │ ├── generator │ │ │ └── CodeGenerator.java │ │ ├── handler │ │ │ ├── GlobalExceptionHandler.java │ │ │ └── ResponseStat.java │ │ ├── interceptor │ │ │ └── PaginationInterceptorImpl.java │ │ ├── options │ │ │ └── DataScopeOptions.java │ │ ├── properties │ │ │ ├── FebsProperties.java │ │ │ ├── ShiroProperties.java │ │ │ └── SwaggerProperties.java │ │ ├── runner │ │ │ ├── CacheInitRunner.java │ │ │ └── StartedUpRunner.java │ │ ├── service │ │ │ ├── CacheService.java │ │ │ ├── RedisService.java │ │ │ └── impl │ │ │ │ ├── CacheServiceImpl.java │ │ │ │ └── RedisServiceImpl.java │ │ ├── task │ │ │ └── CacheTask.java │ │ ├── utils │ │ │ ├── AddressUtil.java │ │ │ ├── AesEncryptUtil.java │ │ │ ├── DateUtil.java │ │ │ ├── EncryptUtil.java │ │ │ ├── FebsUtil.java │ │ │ ├── HttpContextUtil.java │ │ │ ├── HttpUtil.java │ │ │ ├── IPUtil.java │ │ │ ├── MD5Util.java │ │ │ ├── SortUtil.java │ │ │ ├── SpringContextUtil.java │ │ │ └── TreeUtil.java │ │ └── validator │ │ │ ├── CronValidator.java │ │ │ └── NotBlankValidator.java │ │ ├── job │ │ ├── config │ │ │ └── ScheduleConfig.java │ │ ├── controller │ │ │ ├── JobController.java │ │ │ └── JobLogController.java │ │ ├── dao │ │ │ ├── JobLogMapper.java │ │ │ └── JobMapper.java │ │ ├── domain │ │ │ ├── Job.java │ │ │ └── JobLog.java │ │ ├── service │ │ │ ├── JobLogService.java │ │ │ ├── JobService.java │ │ │ └── impl │ │ │ │ ├── JobLogServiceImpl.java │ │ │ │ └── JobServiceImpl.java │ │ ├── task │ │ │ └── TestTask.java │ │ └── util │ │ │ ├── ScheduleJob.java │ │ │ ├── ScheduleRunnable.java │ │ │ └── ScheduleUtils.java │ │ ├── system │ │ ├── controller │ │ │ ├── DeptController.java │ │ │ ├── DictController.java │ │ │ ├── LogController.java │ │ │ ├── LoginController.java │ │ │ ├── MenuController.java │ │ │ ├── RedisController.java │ │ │ ├── RoleController.java │ │ │ ├── TestController.java │ │ │ └── UserController.java │ │ ├── dao │ │ │ ├── CityMapper.java │ │ │ ├── DeptMapper.java │ │ │ ├── DictMapper.java │ │ │ ├── LogMapper.java │ │ │ ├── LoginLogMapper.java │ │ │ ├── MenuMapper.java │ │ │ ├── RoleMapper.java │ │ │ ├── RoleMenuMapper.java │ │ │ ├── TestMapper.java │ │ │ ├── UserConfigMapper.java │ │ │ ├── UserMapper.java │ │ │ └── UserRoleMapper.java │ │ ├── domain │ │ │ ├── City.java │ │ │ ├── Dept.java │ │ │ ├── DeptUsers.java │ │ │ ├── Dict.java │ │ │ ├── LoginLog.java │ │ │ ├── Menu.java │ │ │ ├── Role.java │ │ │ ├── RoleMenu.java │ │ │ ├── SysLog.java │ │ │ ├── Test.java │ │ │ ├── User.java │ │ │ ├── UserConfig.java │ │ │ └── UserRole.java │ │ ├── manager │ │ │ └── UserManager.java │ │ └── service │ │ │ ├── CityService.java │ │ │ ├── DeptService.java │ │ │ ├── DictService.java │ │ │ ├── LogService.java │ │ │ ├── LoginLogService.java │ │ │ ├── MenuService.java │ │ │ ├── RoleMenuServie.java │ │ │ ├── RoleService.java │ │ │ ├── TestService.java │ │ │ ├── UserConfigService.java │ │ │ ├── UserRoleService.java │ │ │ ├── UserService.java │ │ │ └── impl │ │ │ ├── DeptServiceImpl.java │ │ │ ├── DictServiceImpl.java │ │ │ ├── LogServiceImpl.java │ │ │ ├── LoginLogServiceImpl.java │ │ │ ├── MenuServiceImpl.java │ │ │ ├── RoleMenuServiceImpl.java │ │ │ ├── RoleServiceImpl.java │ │ │ ├── TestServiceImpl.java │ │ │ ├── UserConfigServiceImpl.java │ │ │ ├── UserRoleServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ └── web │ │ └── controller │ │ ├── ArticleController.java │ │ ├── CityController.java │ │ ├── CityService.java │ │ ├── CityServiceImpl.java │ │ ├── MovieController.java │ │ └── WeatherController.java │ └── resources │ ├── ValidationMessages.properties │ ├── application.yml │ ├── banner.txt │ ├── generator │ └── templates │ │ ├── controller.java.ftl │ │ ├── entity.java.ftl │ │ ├── mapper.java.ftl │ │ ├── mapper.xml.ftl │ │ ├── service.java.ftl │ │ └── serviceImpl.java.ftl │ ├── ip2region │ └── ip2region.db │ ├── logback-spring.xml │ ├── mapper │ ├── job │ │ └── JobMapper.xml │ └── system │ │ ├── LoginLogMapper.xml │ │ ├── MenuMapper.xml │ │ ├── RoleMapper.xml │ │ ├── UserMapper.xml │ │ └── UserRoleMapper.xml │ └── spy.properties ├── frontend ├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .postcssrc.js ├── build │ ├── build.js │ ├── check-versions.js │ ├── logo.png │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── dev.env.js │ ├── index.js │ ├── prod.env.js │ └── test.env.js ├── index.html ├── package.json ├── src │ ├── FEBS.vue │ ├── components │ │ ├── checkbox │ │ │ ├── ColorCheckbox.vue │ │ │ └── ImgCheckbox.vue │ │ ├── datetime │ │ │ └── RangeDate.vue │ │ ├── exception │ │ │ ├── ExceptionPage.vue │ │ │ └── typeConfig.js │ │ ├── menu │ │ │ ├── Contextmenu.vue │ │ │ ├── SiderMenu.vue │ │ │ └── menu.js │ │ ├── setting │ │ │ ├── Setting.vue │ │ │ ├── SettingItem.vue │ │ │ └── StyleItem.vue │ │ └── tool │ │ │ └── Drawer.vue │ ├── main.js │ ├── router │ │ └── index.js │ ├── store │ │ ├── index.js │ │ └── modules │ │ │ ├── account.js │ │ │ ├── dict.js │ │ │ └── setting.js │ ├── utils │ │ ├── aesEncrypt.js │ │ ├── color.js │ │ ├── common.js │ │ ├── device.js │ │ ├── install.js │ │ ├── localstorage.js │ │ ├── permissionDirect.js │ │ ├── request.js │ │ └── utils.less │ └── views │ │ ├── HomePage.vue │ │ ├── common │ │ ├── EmptyPageView.vue │ │ ├── GlobalFooter.vue │ │ ├── GlobalHeader.vue │ │ ├── GlobalLayout.vue │ │ ├── HeadInfo.vue │ │ ├── HeaderAvatar.vue │ │ ├── MenuView.vue │ │ ├── PageContent.vue │ │ ├── PageLayout.vue │ │ ├── PageView.vue │ │ └── RunningTask.vue │ │ ├── error │ │ ├── 403.vue │ │ ├── 404.vue │ │ └── 500.vue │ │ ├── login │ │ ├── Common.vue │ │ ├── Login.vue │ │ └── Regist.vue │ │ ├── monitor │ │ ├── Httptrace.vue │ │ ├── JvmInfo.vue │ │ ├── Online.vue │ │ ├── RedisInfo.vue │ │ ├── RedisTerminal.vue │ │ ├── RequestLog.vue │ │ ├── SystemInfo.vue │ │ ├── SystemLog.vue │ │ └── TomcatInfo.vue │ │ ├── others │ │ ├── Excel.vue │ │ └── ImportResult.vue │ │ ├── personal │ │ ├── Profile.vue │ │ ├── UpdateAvatar.vue │ │ ├── UpdatePassword.vue │ │ └── UpdateProfile.vue │ │ ├── quartz │ │ ├── job │ │ │ ├── Job.vue │ │ │ ├── JobAdd.vue │ │ │ └── JobEdit.vue │ │ └── log │ │ │ └── JobLog.vue │ │ ├── system │ │ ├── dept │ │ │ ├── Dept.vue │ │ │ ├── DeptAdd.vue │ │ │ ├── DeptEdit.vue │ │ │ └── DeptInputTree.vue │ │ ├── dict │ │ │ ├── Dict.vue │ │ │ ├── DictAdd.vue │ │ │ └── DictEdit.vue │ │ ├── menu │ │ │ ├── ButtonAdd.vue │ │ │ ├── ButtonEdit.vue │ │ │ ├── Icon.less │ │ │ ├── Icons.vue │ │ │ ├── Menu.vue │ │ │ ├── MenuAdd.vue │ │ │ └── MenuEdit.vue │ │ ├── role │ │ │ ├── Role.vue │ │ │ ├── RoleAdd.vue │ │ │ ├── RoleEdit.vue │ │ │ └── RoleInfo.vue │ │ └── user │ │ │ ├── User.vue │ │ │ ├── UserAdd.vue │ │ │ ├── UserEdit.vue │ │ │ ├── UserInfo.less │ │ │ └── UserInfo.vue │ │ └── web │ │ ├── City.vue │ │ ├── CityAdd.vue │ │ ├── CityEdit.vue │ │ ├── DailyArticle.vue │ │ ├── ImportResult.vue │ │ ├── MovieComing.vue │ │ ├── MovieHot.vue │ │ └── Weather.vue └── static │ ├── .gitkeep │ ├── avatar │ ├── 17e420c250804efe904a09a33796d5a10.jpg │ ├── 17e420c250804efe904a09a33796d5a16.jpg │ ├── 19034103295190235.jpg │ ├── 1d22f3e41d284f50b2c8fc32e0788698.jpeg │ ├── 20180414165754.jpg │ ├── 20180414165815.jpg │ ├── 20180414165821.jpg │ ├── 20180414165827.jpg │ ├── 20180414165834.jpg │ ├── 20180414165840.jpg │ ├── 20180414165846.jpg │ ├── 20180414165855.jpg │ ├── 20180414165909.jpg │ ├── 20180414165914.jpg │ ├── 20180414165920.jpg │ ├── 20180414165927.jpg │ ├── 20180414165936.jpg │ ├── 20180414165942.jpg │ ├── 20180414165947.jpg │ ├── 20180414165955.jpg │ ├── 20180414170003.jpg │ ├── 2dd7a2d09fa94bf8b5c52e5318868b4d9.jpg │ ├── 2dd7a2d09fa94bf8b5c52e5318868b4df.jpg │ ├── 496b3ace787342f7954b7045b8b06804.jpeg │ ├── 595ba7b05f2e485eb50565a50cb6cc3c.jpeg │ ├── 5997fedcc7bd4cffbd350b40d1b5b9824.jpg │ ├── 5997fedcc7bd4cffbd350b40d1b5b987.jpg │ ├── 87d8194bc9834e9f8f0228e9e530beb1.jpeg │ ├── 8f5b60ef00714a399ee544d331231820.jpeg │ ├── 964e40b005724165b8cf772355796c8c.jpeg │ ├── BiazfanxmamNRoxxVxka.png │ ├── WhxKECPNujWoWEFNdnJE.png │ ├── a3b10296862e40edb811418d64455d00.jpeg │ ├── a43456282d684e0b9319cf332f8ac468.jpeg │ ├── bba284ac05b041a8b8b0d1927868d5c9x.jpg │ ├── c7c4ee7be3eb4e73a19887dc713505145.jpg │ ├── cnrhVkzwxjPwAaCfPbdc.png │ ├── default.jpg │ ├── ff698bb2d25c4d218b3256b46c706ece.jpeg │ ├── gaOngJwsRYRaVAuXXcmB.png │ ├── jZUIxmJycoymBprLOUbT.png │ └── ubnKSIfAJTxIgXOKlciN.png │ ├── file │ └── city.json │ ├── img │ ├── favicon.ico │ ├── logo-blue.png │ ├── logo.png │ ├── side-bar-dark.svg │ ├── side-bar-left.svg │ ├── side-bar-light.svg │ └── side-bar-top.svg │ └── less │ ├── Color.less │ ├── Common.less │ └── less.min.js ├── images ├── 1.png ├── 10.png ├── 2.png ├── 3.png ├── 4.png ├── 6.png ├── 7.png ├── 8.png ├── 9.png ├── QQ.jpg └── request.png └── sql └── febs.sql /.gitignore: -------------------------------------------------------------------------------- 1 | backend/.idea/ 2 | backend/febs_shiro_jwt.iml 3 | backend/target/ 4 | backend/log/ 5 | 6 | frontend/.DS_Store 7 | frontend/node_modules 8 | frontend/dist/ 9 | frontend/npm-debug.log* 10 | frontend/yarn-debug.log* 11 | frontend/yarn-error.log* 12 | frontend/test/unit/coverage 13 | frontend/test/e2e/reports 14 | frontend/selenium-debug.log 15 | 16 | # Editor directories and files 17 | frontend/.idea 18 | frontend/.vscode 19 | frontend/package-lock.json 20 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/FebsApplication.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | import org.springframework.boot.builder.SpringApplicationBuilder; 5 | import org.springframework.scheduling.annotation.EnableAsync; 6 | import org.springframework.scheduling.annotation.EnableScheduling; 7 | import org.springframework.transaction.annotation.EnableTransactionManagement; 8 | 9 | @SpringBootApplication 10 | @EnableTransactionManagement 11 | @EnableScheduling 12 | @EnableAsync 13 | public class FebsApplication { 14 | 15 | public static void main(String[] args) { 16 | new SpringApplicationBuilder(FebsApplication.class) 17 | .run(args); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/annotation/DataFilter.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.annotation; 2 | 3 | import cc.mrbird.febs.common.enums.FilterType; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 数据范围过滤注解 12 | */ 13 | @Target({ElementType.TYPE,ElementType.METHOD}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface DataFilter { 16 | /** 17 | * 过滤类型 18 | * @return FilterType 19 | */ 20 | FilterType filterType() default FilterType.FIELD;// field, join 21 | 22 | /** 23 | * 过滤字段以及别名 24 | * @return String 25 | */ 26 | String filterFieldId() default "creator"; 27 | 28 | /** 29 | * 过滤方法名 30 | * @return String[] 31 | */ 32 | String[] filterMethods() default {}; 33 | 34 | /** 35 | * 排除过滤方法名 36 | * @return 37 | */ 38 | String[] ruledOutMethods() default {}; 39 | 40 | /** 41 | * 过滤类型为join 的时候 left join的表的sql 42 | * @return 43 | */ 44 | String joinSql() default ""; 45 | } 46 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/annotation/IsCron.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.annotation; 2 | 3 | import cc.mrbird.febs.common.validator.CronValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | @Target({ElementType.FIELD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Constraint(validatedBy = CronValidator.class) 15 | public @interface IsCron { 16 | 17 | String message(); 18 | 19 | Class[] groups() default {}; 20 | 21 | Class[] payload() default {}; 22 | } 23 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/annotation/Limit.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.annotation; 2 | 3 | import cc.mrbird.febs.common.domain.LimitType; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Target(ElementType.METHOD) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | public @interface Limit { 13 | 14 | // 资源名称,用于描述接口功能 15 | String name() default ""; 16 | 17 | // 资源 key 18 | String key() default ""; 19 | 20 | // key prefix 21 | String prefix() default ""; 22 | 23 | // 时间的,单位秒 24 | int period(); 25 | 26 | // 限制访问次数 27 | int count(); 28 | 29 | // 限制类型 30 | LimitType limitType() default LimitType.CUSTOMER; 31 | } 32 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/annotation/Log.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.METHOD) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Log { 11 | String value() default ""; 12 | } 13 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/annotation/NotBlank.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.annotation; 2 | 3 | 4 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 5 | import static java.lang.annotation.ElementType.CONSTRUCTOR; 6 | import static java.lang.annotation.ElementType.FIELD; 7 | import static java.lang.annotation.ElementType.METHOD; 8 | import static java.lang.annotation.ElementType.PARAMETER; 9 | import static java.lang.annotation.ElementType.TYPE_USE; 10 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 11 | 12 | import java.lang.annotation.Documented; 13 | import java.lang.annotation.Repeatable; 14 | import java.lang.annotation.Retention; 15 | import java.lang.annotation.Target; 16 | 17 | import javax.validation.Constraint; 18 | import javax.validation.Payload; 19 | import cc.mrbird.febs.common.annotation.NotBlank.List; 20 | import cc.mrbird.febs.common.validator.NotBlankValidator; 21 | 22 | @Documented 23 | @Constraint(validatedBy = { NotBlankValidator.class}) 24 | @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) 25 | @Retention(RUNTIME) 26 | @Repeatable(List.class) 27 | /** 28 | * The annotated element must not be {@code null} and must contain at least one 29 | * non-whitespace character. Accepts {@code CharSequence}. 30 | * 31 | * @author Hardy Ferentschik 32 | * @since 2.0 33 | * 34 | * @see Character#isWhitespace(char) 35 | */ 36 | public @interface NotBlank { 37 | 38 | String message() default "{cc.mrbird.febs.common.annotation.NotBlank.message}"; 39 | 40 | Class[] groups() default { }; 41 | 42 | Class[] payload() default { }; 43 | 44 | /** 45 | * Defines several {@code @NotBlank} constraints on the same element. 46 | * 47 | * @see NotBlank 48 | */ 49 | @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) 50 | @Retention(RUNTIME) 51 | @Documented 52 | public @interface List { 53 | NotBlank[] value(); 54 | } 55 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/aspect/LogAspect.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.aspect; 2 | 3 | import cc.mrbird.febs.common.authentication.JWTUtil; 4 | import cc.mrbird.febs.common.properties.FebsProperties; 5 | import cc.mrbird.febs.common.utils.HttpContextUtil; 6 | import cc.mrbird.febs.common.utils.IPUtil; 7 | import cc.mrbird.febs.system.domain.SysLog; 8 | import cc.mrbird.febs.system.service.LogService; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.apache.shiro.SecurityUtils; 12 | import org.aspectj.lang.ProceedingJoinPoint; 13 | import org.aspectj.lang.annotation.Around; 14 | import org.aspectj.lang.annotation.Aspect; 15 | import org.aspectj.lang.annotation.Pointcut; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Component; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | 21 | /** 22 | * AOP 记录用户操作日志 23 | * 24 | * @author MrBird 25 | * @link https://mrbird.cc/Spring-Boot-AOP%20log.html 26 | */ 27 | @Slf4j 28 | @Aspect 29 | @Component 30 | public class LogAspect { 31 | 32 | @Autowired 33 | private FebsProperties febsProperties; 34 | 35 | @Autowired 36 | private LogService logService; 37 | 38 | @Pointcut("@annotation(cc.mrbird.febs.common.annotation.Log)") 39 | public void pointcut() { 40 | // do nothing 41 | } 42 | 43 | @Around("pointcut()") 44 | public Object around(ProceedingJoinPoint point) throws Throwable { 45 | Object result = null; 46 | long beginTime = System.currentTimeMillis(); 47 | // 执行方法 48 | result = point.proceed(); 49 | // 获取 request 50 | HttpServletRequest request = HttpContextUtil.getHttpServletRequest(); 51 | // 设置 IP 地址 52 | String ip = IPUtil.getIpAddr(request); 53 | // 执行时长(毫秒) 54 | long time = System.currentTimeMillis() - beginTime; 55 | if (febsProperties.isOpenAopLog()) { 56 | // 保存日志 57 | String token = (String) SecurityUtils.getSubject().getPrincipal(); 58 | String username = ""; 59 | if (StringUtils.isNotBlank(token)) { 60 | username = JWTUtil.getUsername(token); 61 | } 62 | 63 | SysLog log = new SysLog(); 64 | log.setUsername(username); 65 | log.setIp(ip); 66 | log.setTime(time); 67 | logService.saveLog(point, log); 68 | } 69 | return result; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/authentication/JWTToken.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.authentication; 2 | 3 | import lombok.Data; 4 | import org.apache.shiro.authc.AuthenticationToken; 5 | 6 | /** 7 | * JSON Web Token 8 | */ 9 | @Data 10 | public class JWTToken implements AuthenticationToken { 11 | 12 | private static final long serialVersionUID = 1282057025599826155L; 13 | 14 | private String token; 15 | 16 | private String exipreAt; 17 | 18 | public JWTToken(String token) { 19 | this.token = token; 20 | } 21 | 22 | public JWTToken(String token, String exipreAt) { 23 | this.token = token; 24 | this.exipreAt = exipreAt; 25 | } 26 | 27 | @Override 28 | public Object getPrincipal() { 29 | return token; 30 | } 31 | 32 | @Override 33 | public Object getCredentials() { 34 | return token; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/authentication/JWTUtil.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.authentication; 2 | 3 | import cc.mrbird.febs.common.properties.FebsProperties; 4 | import cc.mrbird.febs.common.utils.SpringContextUtil; 5 | import com.auth0.jwt.JWT; 6 | import com.auth0.jwt.JWTVerifier; 7 | import com.auth0.jwt.algorithms.Algorithm; 8 | import com.auth0.jwt.exceptions.JWTDecodeException; 9 | import com.auth0.jwt.interfaces.DecodedJWT; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import java.util.Date; 14 | 15 | @Slf4j 16 | public class JWTUtil { 17 | 18 | private static final long EXPIRE_TIME = SpringContextUtil.getBean(FebsProperties.class).getShiro().getJwtTimeOut() * 1000; 19 | 20 | /** 21 | * 校验 token是否正确 22 | * 23 | * @param token 密钥 24 | * @param secret 用户的密码 25 | * @return 是否正确 26 | */ 27 | public static boolean verify(String token, String username, String secret) { 28 | try { 29 | Algorithm algorithm = Algorithm.HMAC256(secret); 30 | JWTVerifier verifier = JWT.require(algorithm) 31 | .withClaim("username", username) 32 | .build(); 33 | verifier.verify(token); 34 | log.info("token is valid"); 35 | return true; 36 | } catch (Exception e) { 37 | log.info("token is invalid{}", e.getMessage()); 38 | return false; 39 | } 40 | } 41 | 42 | /** 43 | * 从 token中获取用户名 44 | * 45 | * @return token中包含的用户名 46 | */ 47 | public static String getUsername(String token) { 48 | try { 49 | DecodedJWT jwt = JWT.decode(token); 50 | return jwt.getClaim("username").asString(); 51 | } catch (JWTDecodeException e) { 52 | log.error("error:{}", e.getMessage()); 53 | return null; 54 | } 55 | } 56 | 57 | /** 58 | * 生成 token 59 | * 60 | * @param username 用户名 61 | * @param secret 用户的密码 62 | * @return token 63 | */ 64 | public static String sign(String username, String secret) { 65 | try { 66 | username = StringUtils.lowerCase(username); 67 | Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); 68 | Algorithm algorithm = Algorithm.HMAC256(secret); 69 | return JWT.create() 70 | .withClaim("username", username) 71 | .withExpiresAt(date) 72 | .sign(algorithm); 73 | } catch (Exception e) { 74 | log.error("error:{}", e); 75 | return null; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/authentication/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.authentication; 2 | 3 | import org.apache.shiro.mgt.SecurityManager; 4 | import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 5 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 6 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import javax.servlet.Filter; 11 | import java.util.LinkedHashMap; 12 | 13 | /** 14 | * Shiro 配置类 15 | * 16 | * @author MrBird 17 | */ 18 | @Configuration 19 | public class ShiroConfig { 20 | 21 | @Bean 22 | public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { 23 | ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 24 | // 设置 securityManager 25 | shiroFilterFactoryBean.setSecurityManager(securityManager); 26 | 27 | // 在 Shiro过滤器链上加入 JWTFilter 28 | LinkedHashMap filters = new LinkedHashMap<>(); 29 | filters.put("jwt", new JWTFilter()); 30 | shiroFilterFactoryBean.setFilters(filters); 31 | 32 | LinkedHashMap filterChainDefinitionMap = new LinkedHashMap<>(); 33 | // 所有请求都要经过 jwt过滤器 34 | filterChainDefinitionMap.put("/**", "jwt"); 35 | 36 | shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 37 | return shiroFilterFactoryBean; 38 | } 39 | 40 | @Bean 41 | public SecurityManager securityManager() { 42 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 43 | // 配置 SecurityManager,并注入 shiroRealm 44 | securityManager.setRealm(shiroRealm()); 45 | return securityManager; 46 | } 47 | 48 | @Bean 49 | public ShiroRealm shiroRealm() { 50 | // 配置 Realm 51 | return new ShiroRealm(); 52 | } 53 | 54 | @Bean 55 | public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { 56 | AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); 57 | authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); 58 | return authorizationAttributeSourceAdvisor; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/config/AsyncExecutorPoolConfig.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.scheduling.annotation.AsyncConfigurerSupport; 6 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 7 | 8 | import java.util.concurrent.Executor; 9 | import java.util.concurrent.ThreadPoolExecutor; 10 | 11 | @Configuration 12 | public class AsyncExecutorPoolConfig extends AsyncConfigurerSupport { 13 | @Bean 14 | public Executor taskExecutor() { 15 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 16 | 17 | executor.setCorePoolSize(5); 18 | executor.setMaxPoolSize(20); 19 | executor.setQueueCapacity(100); 20 | executor.setKeepAliveSeconds(30); 21 | executor.setThreadNamePrefix("asyncTaskExecutor-"); 22 | 23 | executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 24 | return executor; 25 | } 26 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.config; 2 | 3 | import cc.mrbird.febs.common.interceptor.PaginationInterceptorImpl; 4 | import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; 5 | import org.mybatis.spring.annotation.MapperScan; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @MapperScan(value = {"cc.mrbird.febs.*.dao"}) 11 | public class MybatisPlusConfig { 12 | 13 | /** 14 | * 分页插件 15 | */ 16 | @Bean 17 | public PaginationInterceptor paginationInterceptor() { 18 | return new PaginationInterceptorImpl(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/config/P6spySqlFormatConfig.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.config; 2 | 3 | import cc.mrbird.febs.common.utils.DateUtil; 4 | import com.p6spy.engine.spy.appender.MessageFormattingStrategy; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | /** 10 | * 自定义 p6spy sql输出格式 11 | * 12 | * @author MrBird 13 | */ 14 | public class P6spySqlFormatConfig implements MessageFormattingStrategy { 15 | 16 | /** 17 | * 过滤掉定时任务的 SQL 18 | */ 19 | @Override 20 | public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) { 21 | return StringUtils.isNotBlank(sql) ? DateUtil.formatFullTime(LocalDateTime.now(), DateUtil.FULL_TIME_SPLIT_PATTERN) 22 | + " | 耗时 " + elapsed + " ms | SQL 语句:" + StringUtils.LF + sql.replaceAll("[\\s]+", StringUtils.SPACE) + ";" : ""; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.controller; 2 | 3 | import com.baomidou.mybatisplus.core.metadata.IPage; 4 | import org.springframework.beans.factory.annotation.Value; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class BaseController { 10 | 11 | //每页的最大导出数量 12 | @Value("${export.maxCount}") 13 | public Integer exportMaxCount; 14 | 15 | protected Map getDataTable(IPage pageInfo) { 16 | Map rspData = new HashMap<>(); 17 | rspData.put("rows", pageInfo.getRecords()); 18 | rspData.put("total", pageInfo.getTotal()); 19 | return rspData; 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/converter/DataScopeReadConverter.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.converter; 2 | 3 | import com.wuwenze.poi.convert.ReadConverter; 4 | import com.wuwenze.poi.exception.ExcelKitReadConverterException; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | /** 8 | * @author : wx 9 | * @version : 1 10 | * @date : 2019/9/27 15:45 11 | */ 12 | public class DataScopeReadConverter implements ReadConverter { 13 | @Override 14 | public Object convert(Object value) throws ExcelKitReadConverterException { 15 | return value==null?"": StringUtils.trim(value.toString()).equals("全部数据")?"0":StringUtils.trim(value.toString()).equals("部门数据")?"1":StringUtils.trim(value.toString()).equals("个人数据")?"2":"-1"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/converter/DataScopeWriteConverter.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.converter; 2 | 3 | import com.wuwenze.poi.convert.WriteConverter; 4 | import com.wuwenze.poi.exception.ExcelKitWriteConverterException; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | /** 8 | * @author : wx 9 | * @version : 1 10 | * @date : 2019/9/27 15:40 11 | */ 12 | public class DataScopeWriteConverter implements WriteConverter { 13 | @Override 14 | public String convert(Object value) throws ExcelKitWriteConverterException { 15 | return value==null?"": StringUtils.trim(value.toString()).equals("0")?"全部数据":StringUtils.trim(value.toString()).equals("1")?"部门数据":StringUtils.trim(value.toString()).equals("2")?"个人数据":"未知数据范围"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/converter/TimeConverter.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.converter; 2 | 3 | import cc.mrbird.febs.common.utils.DateUtil; 4 | import com.wuwenze.poi.convert.WriteConverter; 5 | import com.wuwenze.poi.exception.ExcelKitWriteConverterException; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | /** 9 | * Execl导出时间类型字段格式化 10 | */ 11 | @Slf4j 12 | public class TimeConverter implements WriteConverter { 13 | @Override 14 | public String convert(Object value) throws ExcelKitWriteConverterException { 15 | try { 16 | if (value == null) 17 | return ""; 18 | else { 19 | return DateUtil.formatCSTTime(value.toString(), DateUtil.FULL_TIME_SPLIT_PATTERN); 20 | } 21 | } catch (Exception e) { 22 | log.error("时间转换异常", e); 23 | return ""; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/ActiveUser.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain; 2 | 3 | import cc.mrbird.febs.common.utils.DateUtil; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.Data; 6 | import org.apache.commons.lang3.RandomStringUtils; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | * 在线用户 13 | */ 14 | @Data 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class ActiveUser implements Serializable { 17 | private static final long serialVersionUID = 2055229953429884344L; 18 | 19 | // 唯一编号 20 | private String id = RandomStringUtils.randomAlphanumeric(20); 21 | // 用户名 22 | private String username; 23 | // ip地址 24 | private String ip; 25 | // token(加密后) 26 | private String token; 27 | // 登录时间 28 | private String loginTime = DateUtil.formatFullTime(LocalDateTime.now(),DateUtil.FULL_TIME_SPLIT_PATTERN); 29 | // 登录地点 30 | private String loginAddress; 31 | } 32 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/FebsConstant.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain; 2 | 3 | /** 4 | * FEBS常量 5 | */ 6 | public class FebsConstant { 7 | public static final String UNDER_LINE = "_"; 8 | // user缓存前缀 9 | public static final String USER_CACHE_PREFIX = "febs.cache.user."; 10 | // user角色缓存前缀 11 | public static final String USER_ROLE_CACHE_PREFIX = "febs.cache.user.role."; 12 | // user权限缓存前缀 13 | public static final String USER_PERMISSION_CACHE_PREFIX = "febs.cache.user.permission."; 14 | // user部门数据权限缓存前缀 15 | public static final String USER_PERMISSION_DEPT_DATA_CACHE_PREFIX = "febs.cache.user.dept.data.permission."; 16 | // user个性化配置前缀 17 | public static final String USER_CONFIG_CACHE_PREFIX = "febs.cache.user.config."; 18 | // token缓存前缀 19 | public static final String TOKEN_CACHE_PREFIX = "febs.cache.token."; 20 | 21 | // 存储在线用户的 zset前缀 22 | public static final String ACTIVE_USERS_ZSET_PREFIX = "febs.user.active"; 23 | 24 | // 排序规则: descend 降序 25 | public static final String ORDER_DESC = "descend"; 26 | // 排序规则: ascend 升序 27 | public static final String ORDER_ASC = "ascend"; 28 | 29 | // 按钮 30 | public static final String TYPE_BUTTON = "1"; 31 | // 菜单 32 | public static final String TYPE_MENU = "0"; 33 | 34 | // 网络资源 Url 35 | public static final String MEIZU_WEATHER_URL = "http://aider.meizu.com/app/weather/listWeather"; 36 | public static final String MRYW_TODAY_URL = "https://interface.meiriyiwen.com/article/today"; 37 | public static final String MRYW_DAY_URL = "https://interface.meiriyiwen.com/article/day"; 38 | public static final String TIME_MOVIE_HOT_URL = "https://api-m.mtime.cn/Showtime/LocationMovies.api"; 39 | public static final String TIME_MOVIE_DETAIL_URL = "https://ticket-api-m.mtime.cn/movie/detail.api"; 40 | public static final String TIME_MOVIE_COMING_URL = "https://api-m.mtime.cn/Movie/MovieComingNew.api"; 41 | public static final String TIME_MOVIE_COMMENTS_URL = "https://ticket-api-m.mtime.cn/movie/hotComment.api"; 42 | //数据范围权限 43 | public static final int DATA_FILTER_ALL=0; 44 | public static final int DATA_FILTER_DEPT=1; 45 | public static final int DATA_FILTER_OWN=2; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/FebsResponse.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain; 2 | 3 | import java.util.HashMap; 4 | 5 | public class FebsResponse extends HashMap { 6 | 7 | private static final long serialVersionUID = -8713837118340960775L; 8 | 9 | public FebsResponse message(String message) { 10 | this.put("message", message); 11 | return this; 12 | } 13 | 14 | public FebsResponse code(String code) { 15 | this.put("code", code); 16 | return this; 17 | } 18 | 19 | public FebsResponse status(String status) { 20 | this.put("status", status); 21 | return this; 22 | } 23 | 24 | public FebsResponse data(Object data) { 25 | this.put("data", data); 26 | return this; 27 | } 28 | 29 | @Override 30 | public FebsResponse put(String key, Object value) { 31 | super.put(key, value); 32 | return this; 33 | } 34 | 35 | public FebsResponse addCodeMessage(Integer code,String message,String status,Object data) { 36 | this.put("code", code); 37 | this.put("message", message); 38 | this.put("status", status); 39 | this.put("data", data); 40 | return this; 41 | } 42 | 43 | public FebsResponse addCodeMessage(Integer code,String message,String status) { 44 | this.put("code", code); 45 | this.put("message", message); 46 | this.put("status", status); 47 | return this; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/LimitType.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain; 2 | 3 | public enum LimitType { 4 | // 传统类型 5 | CUSTOMER, 6 | // 根据 IP 限制 7 | IP; 8 | } 9 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/QueryRequest.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain; 2 | 3 | import lombok.Data; 4 | import lombok.ToString; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | public class QueryRequest implements Serializable { 10 | 11 | private static final long serialVersionUID = -4869594085374385813L; 12 | 13 | private int pageSize = 10; 14 | private int pageNum = 1; 15 | 16 | private String sortField; 17 | private String sortOrder; 18 | } 19 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/RegexpConstant.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain; 2 | 3 | /** 4 | * 正则常量 5 | */ 6 | public class RegexpConstant { 7 | 8 | // 简单手机号正则(这里只是简单校验是否为11位,实际规则很复杂) 9 | public static final String MOBILE_REG = "[1]\\d{10}"; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/Tree.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | import lombok.ToString; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Date; 9 | import java.util.List; 10 | 11 | @Data 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class Tree { 14 | 15 | private String id; 16 | 17 | private String key; 18 | 19 | private String icon; 20 | 21 | private String title; 22 | 23 | private String value; 24 | 25 | private String text; 26 | 27 | private String permission; 28 | 29 | private String type; 30 | 31 | private Double order; 32 | 33 | private String path; 34 | 35 | private String component; 36 | 37 | private List> children; 38 | 39 | private String parentId; 40 | 41 | private boolean hasParent = false; 42 | 43 | private boolean hasChildren = false; 44 | 45 | private Date createTime; 46 | 47 | private Date modifyTime; 48 | 49 | public void initChildren(){ 50 | this.children = new ArrayList<>(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/router/RouterMeta.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain.router; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * Vue路由 Meta 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | public class RouterMeta implements Serializable { 16 | 17 | private static final long serialVersionUID = 5499925008927195914L; 18 | 19 | private Boolean closeable; 20 | 21 | private Boolean isShow; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/domain/router/VueRouter.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.domain.router; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.Data; 6 | import lombok.ToString; 7 | 8 | import java.io.Serializable; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * 构建 Vue路由 14 | */ 15 | @Data 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | public class VueRouter implements Serializable { 18 | 19 | private static final long serialVersionUID = -3327478146308500708L; 20 | 21 | @JsonIgnore 22 | private String id; 23 | 24 | @JsonIgnore 25 | private String parentId; 26 | 27 | private String path; 28 | 29 | private String name; 30 | 31 | private String component; 32 | 33 | private String icon; 34 | 35 | private String redirect; 36 | 37 | private RouterMeta meta; 38 | 39 | private List> children; 40 | 41 | @JsonIgnore 42 | private boolean hasParent = false; 43 | 44 | @JsonIgnore 45 | private boolean hasChildren = false; 46 | 47 | public void initChildren(){ 48 | this.children = new ArrayList<>(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/enums/FilterType.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.enums; 2 | 3 | public enum FilterType { 4 | FIELD("field"), 5 | JOIN("join"); 6 | private String type; 7 | FilterType(String type){ 8 | this.type=type; 9 | } 10 | 11 | public String getType() { 12 | return type; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/exception/FebsException.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.exception; 2 | 3 | /** 4 | * FEBS 系统内部异常 5 | */ 6 | public class FebsException extends Exception { 7 | 8 | private static final long serialVersionUID = -994962710559017255L; 9 | 10 | public FebsException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/exception/LimitAccessException.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.exception; 2 | 3 | /** 4 | * 限流异常 5 | */ 6 | public class LimitAccessException extends Exception { 7 | 8 | private static final long serialVersionUID = -3608667856397125671L; 9 | 10 | public LimitAccessException(String message) { 11 | super(message); 12 | } 13 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/exception/RedisConnectException.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.exception; 2 | 3 | /** 4 | * Redis 连接异常 5 | */ 6 | public class RedisConnectException extends Exception { 7 | 8 | private static final long serialVersionUID = 1639374111871115063L; 9 | 10 | public RedisConnectException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/exception/TokenTimeoutException.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.exception; 2 | 3 | import org.apache.shiro.authc.AuthenticationException; 4 | 5 | /** 6 | * token过期抛出这个 7 | */ 8 | public class TokenTimeoutException extends AuthenticationException { 9 | 10 | private static final long serialVersionUID = -8313101744886192005L; 11 | 12 | public TokenTimeoutException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/function/CacheSelector.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.function; 2 | 3 | @FunctionalInterface 4 | public interface CacheSelector { 5 | T select() throws Exception; 6 | } 7 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/function/JedisExecutor.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.function; 2 | 3 | import cc.mrbird.febs.common.exception.RedisConnectException; 4 | 5 | @FunctionalInterface 6 | public interface JedisExecutor { 7 | R excute(T t) throws RedisConnectException; 8 | } 9 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/handler/ResponseStat.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.handler; 2 | 3 | public enum ResponseStat { 4 | SUCCESS("success"), 5 | ERROR("error"); 6 | private String text; 7 | ResponseStat(String text){ 8 | this.text=text; 9 | } 10 | public String getText() { 11 | return text; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/options/DataScopeOptions.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.options; 2 | 3 | import com.wuwenze.poi.config.Options; 4 | 5 | /** 6 | * @author : wx 7 | * @version : 1 8 | * @date : 2019/9/27 15:51 9 | */ 10 | public class DataScopeOptions implements Options { 11 | @Override 12 | public String[] get() { 13 | return new String[]{"个人数据","部门数据","全部数据"}; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/properties/FebsProperties.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Data 9 | @Component 10 | @Configuration 11 | @ConfigurationProperties(prefix = "febs") 12 | public class FebsProperties { 13 | 14 | private ShiroProperties shiro = new ShiroProperties(); 15 | 16 | private boolean openAopLog = true; 17 | 18 | private SwaggerProperties swagger = new SwaggerProperties(); 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/properties/ShiroProperties.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.properties; 2 | 3 | public class ShiroProperties { 4 | 5 | private String anonUrl; 6 | 7 | /** 8 | * token默认有效时间 1天 9 | */ 10 | private Long jwtTimeOut = 86400L; 11 | 12 | public String getAnonUrl() { 13 | return anonUrl; 14 | } 15 | 16 | public void setAnonUrl(String anonUrl) { 17 | this.anonUrl = anonUrl; 18 | } 19 | 20 | public Long getJwtTimeOut() { 21 | return jwtTimeOut; 22 | } 23 | 24 | public void setJwtTimeOut(Long jwtTimeOut) { 25 | this.jwtTimeOut = jwtTimeOut; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/properties/SwaggerProperties.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author MrBird 7 | */ 8 | @Data 9 | public class SwaggerProperties { 10 | private String basePackage; 11 | private String title; 12 | private String description; 13 | private String version; 14 | private String author; 15 | private String url; 16 | private String email; 17 | private String license; 18 | private String licenseUrl; 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/runner/CacheInitRunner.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.runner; 2 | 3 | import cc.mrbird.febs.common.exception.RedisConnectException; 4 | import cc.mrbird.febs.common.service.CacheService; 5 | import cc.mrbird.febs.system.domain.User; 6 | import cc.mrbird.febs.system.manager.UserManager; 7 | import cc.mrbird.febs.system.service.UserService; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.ApplicationArguments; 11 | import org.springframework.boot.ApplicationRunner; 12 | import org.springframework.context.ConfigurableApplicationContext; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * 缓存初始化 21 | */ 22 | @Slf4j 23 | @Component 24 | public class CacheInitRunner implements ApplicationRunner { 25 | 26 | @Autowired 27 | private UserService userService; 28 | 29 | @Autowired 30 | private CacheService cacheService; 31 | @Autowired 32 | private UserManager userManager; 33 | 34 | @Autowired 35 | private ConfigurableApplicationContext context; 36 | 37 | @Override 38 | public void run(ApplicationArguments args) { 39 | try { 40 | log.info("Redis连接中 ······"); 41 | cacheService.testConnect(); 42 | 43 | log.info("缓存初始化 ······"); 44 | log.info("缓存用户数据 ······"); 45 | List list = this.userService.list(); 46 | //缓存用户,部门关系 47 | this.userService.findSubordinatesMap().stream().forEach((map)-> { 48 | try { 49 | cacheService.saveUserSubordinates(map.getDeptId(),map.getUserIds()); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | }); 54 | 55 | for (User user : list) { 56 | userManager.loadUserRedisCache(user); 57 | } 58 | } catch (Exception e) { 59 | log.error("缓存初始化失败,{}", e.getMessage()); 60 | log.error(" ____ __ _ _ "); 61 | log.error("| |_ / /\\ | | | |"); 62 | log.error("|_| /_/--\\ |_| |_|__"); 63 | log.error(" "); 64 | log.error("FEBS启动失败 "); 65 | if (e instanceof RedisConnectException) 66 | log.error("Redis连接异常,请检查Redis连接配置并确保Redis服务已启动"); 67 | // 关闭 FEBS 68 | context.close(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/runner/StartedUpRunner.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.runner; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.ApplicationArguments; 6 | import org.springframework.boot.ApplicationRunner; 7 | import org.springframework.context.ConfigurableApplicationContext; 8 | import org.springframework.core.annotation.Order; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | @Order 14 | @Slf4j 15 | @Component 16 | public class StartedUpRunner implements ApplicationRunner { 17 | 18 | @Autowired 19 | private ConfigurableApplicationContext context; 20 | 21 | @Override 22 | public void run(ApplicationArguments args) { 23 | if (context.isActive()) { 24 | log.info(" __ ___ _ ___ _ ____ _____ ____ "); 25 | log.info("/ /` / / \\ | |\\/| | |_) | | | |_ | | | |_ "); 26 | log.info("\\_\\_, \\_\\_/ |_| | |_| |_|__ |_|__ |_| |_|__ "); 27 | log.info(" "); 28 | log.info("FEBS 启动完毕,时间:" + LocalDateTime.now()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/task/CacheTask.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.task; 2 | 3 | import cc.mrbird.febs.common.domain.FebsConstant; 4 | import cc.mrbird.febs.common.service.RedisService; 5 | import cc.mrbird.febs.common.utils.DateUtil; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.scheduling.annotation.Scheduled; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | * 主要用于定时删除 Redis中 key为 febs.user.active 中 15 | * 已经过期的 score 16 | */ 17 | @Slf4j 18 | @Component 19 | public class CacheTask { 20 | 21 | @Autowired 22 | private RedisService redisService; 23 | 24 | @Scheduled(fixedRate = 3600000) 25 | public void run() { 26 | try { 27 | String now = DateUtil.formatFullTime(LocalDateTime.now()); 28 | redisService.zremrangeByScore(FebsConstant.ACTIVE_USERS_ZSET_PREFIX, "-inf", now); 29 | log.info("delete expired user"); 30 | } catch (Exception ignore) { 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/utils/AddressUtil.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.io.FileUtils; 5 | import org.lionsoul.ip2region.DataBlock; 6 | import org.lionsoul.ip2region.DbConfig; 7 | import org.lionsoul.ip2region.DbSearcher; 8 | import org.lionsoul.ip2region.Util; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.lang.reflect.Method; 13 | import java.util.Objects; 14 | 15 | @Slf4j 16 | public class AddressUtil { 17 | 18 | public static String getCityInfo(String ip) { 19 | DbSearcher searcher = null; 20 | try { 21 | String dbPath = AddressUtil.class.getResource("/ip2region/ip2region.db").getPath(); 22 | File file = new File(dbPath); 23 | if (!file.exists()) { 24 | String tmpDir = System.getProperties().getProperty("java.io.tmpdir"); 25 | dbPath = tmpDir + "ip.db"; 26 | file = new File(dbPath); 27 | FileUtils.copyInputStreamToFile(Objects.requireNonNull(AddressUtil.class.getClassLoader().getResourceAsStream("classpath:ip2region/ip2region.db")), file); 28 | } 29 | int algorithm = DbSearcher.BTREE_ALGORITHM; 30 | DbConfig config = new DbConfig(); 31 | searcher = new DbSearcher(config, file.getPath()); 32 | Method method = null; 33 | method = searcher.getClass().getMethod("btreeSearch", String.class); 34 | DataBlock dataBlock = null; 35 | if (!Util.isIpAddress(ip)) { 36 | log.error("Error: Invalid ip address"); 37 | } 38 | dataBlock = (DataBlock) method.invoke(searcher, ip); 39 | return dataBlock.getRegion(); 40 | } catch (Exception e) { 41 | log.error("获取地址信息异常", e); 42 | } finally { 43 | if (searcher != null) { 44 | try { 45 | searcher.close(); 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | } 51 | return ""; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/utils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.utils; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.time.LocalDateTime; 6 | import java.time.format.DateTimeFormatter; 7 | import java.util.Date; 8 | import java.util.Locale; 9 | 10 | /** 11 | * 时间工具类 12 | */ 13 | public class DateUtil { 14 | 15 | public static final String FULL_TIME_PATTERN = "yyyyMMddHHmmss"; 16 | 17 | public static final String FULL_TIME_SPLIT_PATTERN = "yyyy-MM-dd HH:mm:ss"; 18 | 19 | public static String formatFullTime(LocalDateTime localDateTime) { 20 | return formatFullTime(localDateTime, FULL_TIME_PATTERN); 21 | } 22 | 23 | public static String formatFullTime(LocalDateTime localDateTime, String pattern) { 24 | DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); 25 | return localDateTime.format(dateTimeFormatter); 26 | } 27 | 28 | private static String getDateFormat(Date date, String dateFormatType) { 29 | SimpleDateFormat simformat = new SimpleDateFormat(dateFormatType); 30 | return simformat.format(date); 31 | } 32 | 33 | public static String formatCSTTime(String date, String format) throws ParseException { 34 | SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US); 35 | Date d = sdf.parse(date); 36 | return DateUtil.getDateFormat(d, format); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/utils/EncryptUtil.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.utils; 2 | 3 | 4 | import javax.crypto.Cipher; 5 | import java.security.Key; 6 | import java.security.Security; 7 | 8 | public class EncryptUtil { 9 | 10 | //设置默认密匙 11 | private static String strDefaultKey = "defaultKey"; 12 | //加密 13 | private Cipher encryptCipher = null; 14 | //解密 15 | private Cipher decryptCipher = null; 16 | 17 | private static String byteArr2HexStr(byte[] arrB) { 18 | int iLen = arrB.length; 19 | StringBuilder sb = new StringBuilder(iLen * 2); 20 | for (byte anArrB : arrB) { 21 | int intTmp = anArrB; 22 | while (intTmp < 0) { 23 | intTmp = intTmp + 256; 24 | } 25 | if (intTmp < 16) { 26 | sb.append("0"); 27 | } 28 | sb.append(Integer.toString(intTmp, 16)); 29 | } 30 | return sb.toString(); 31 | } 32 | 33 | private static byte[] hexStr2ByteArr(String strIn) { 34 | byte[] arrB = strIn.getBytes(); 35 | int iLen = arrB.length; 36 | 37 | byte[] arrOut = new byte[iLen / 2]; 38 | for (int i = 0; i < iLen; i = i + 2) { 39 | String strTmp = new String(arrB, i, 2); 40 | arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16); 41 | } 42 | return arrOut; 43 | } 44 | 45 | public EncryptUtil() throws Exception { 46 | this(strDefaultKey); 47 | } 48 | 49 | EncryptUtil(String strKey) throws Exception { 50 | Security.addProvider(new com.sun.crypto.provider.SunJCE()); 51 | Key key = getKey(strKey.getBytes()); 52 | 53 | encryptCipher = Cipher.getInstance("DES"); 54 | encryptCipher.init(Cipher.ENCRYPT_MODE, key); 55 | 56 | decryptCipher = Cipher.getInstance("DES"); 57 | decryptCipher.init(Cipher.DECRYPT_MODE, key); 58 | } 59 | 60 | private byte[] encrypt(byte[] arrB) throws Exception { 61 | return encryptCipher.doFinal(arrB); 62 | } 63 | 64 | String encrypt(String strIn) throws Exception { 65 | return byteArr2HexStr(encrypt(strIn.getBytes())); 66 | } 67 | 68 | private byte[] decrypt(byte[] arrB) throws Exception { 69 | return decryptCipher.doFinal(arrB); 70 | } 71 | 72 | String decrypt(String strIn) { 73 | try { 74 | return new String(decrypt(hexStr2ByteArr(strIn))); 75 | } catch (Exception e) { 76 | return ""; 77 | } 78 | } 79 | 80 | private Key getKey(byte[] arrBTmp) { 81 | byte[] arrB = new byte[8]; 82 | for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) { 83 | arrB[i] = arrBTmp[i]; 84 | } 85 | return new javax.crypto.spec.SecretKeySpec(arrB, "DES"); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/utils/HttpContextUtil.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.utils; 2 | 3 | import org.springframework.web.context.request.RequestContextHolder; 4 | import org.springframework.web.context.request.ServletRequestAttributes; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.util.Objects; 8 | 9 | public class HttpContextUtil { 10 | 11 | private HttpContextUtil(){ 12 | 13 | } 14 | public static HttpServletRequest getHttpServletRequest() { 15 | return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/utils/IPUtil.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.utils; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | public class IPUtil { 6 | 7 | private static final String UNKNOWN = "unknown"; 8 | 9 | protected IPUtil(){ 10 | 11 | } 12 | 13 | /** 14 | * 获取 IP地址 15 | * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址 16 | * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址, 17 | * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址 18 | */ 19 | public static String getIpAddr(HttpServletRequest request) { 20 | String ip = request.getHeader("x-forwarded-for"); 21 | if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 22 | ip = request.getHeader("Proxy-Client-IP"); 23 | } 24 | if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 25 | ip = request.getHeader("WL-Proxy-Client-IP"); 26 | } 27 | if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 28 | ip = request.getRemoteAddr(); 29 | } 30 | return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/utils/MD5Util.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.utils; 2 | 3 | import org.apache.shiro.crypto.hash.SimpleHash; 4 | import org.apache.shiro.util.ByteSource; 5 | 6 | public class MD5Util { 7 | 8 | protected MD5Util(){ 9 | 10 | } 11 | 12 | private static final String ALGORITH_NAME = "md5"; 13 | 14 | private static final int HASH_ITERATIONS = 2; 15 | 16 | public static String encrypt(String password) { 17 | return new SimpleHash(ALGORITH_NAME, password, ByteSource.Util.bytes(password), HASH_ITERATIONS).toHex(); 18 | } 19 | 20 | public static String encrypt(String username, String password) { 21 | return new SimpleHash(ALGORITH_NAME, password, ByteSource.Util.bytes(username.toLowerCase() + password), 22 | HASH_ITERATIONS).toHex(); 23 | } 24 | 25 | public static void main(String[] args) { 26 | System.out.println(encrypt("mrbird","1234qwer")); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/utils/SpringContextUtil.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.utils; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * Spring Context 工具类 10 | * 11 | * @author MrBird 12 | * 13 | */ 14 | @Component 15 | public class SpringContextUtil implements ApplicationContextAware { 16 | private static ApplicationContext applicationContext; 17 | 18 | @Override 19 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 20 | SpringContextUtil.applicationContext = applicationContext; 21 | } 22 | 23 | public static Object getBean(String name) { 24 | return applicationContext.getBean(name); 25 | } 26 | public static T getBean(Class clazz){ 27 | return applicationContext.getBean(clazz); 28 | } 29 | 30 | public static T getBean(String name, Class requiredType) { 31 | return applicationContext.getBean(name, requiredType); 32 | } 33 | 34 | public static boolean containsBean(String name) { 35 | return applicationContext.containsBean(name); 36 | } 37 | 38 | public static boolean isSingleton(String name) { 39 | return applicationContext.isSingleton(name); 40 | } 41 | 42 | public static Class getType(String name) { 43 | return applicationContext.getType(name); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/validator/CronValidator.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.validator; 2 | 3 | import cc.mrbird.febs.common.annotation.IsCron; 4 | import org.quartz.CronExpression; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | 9 | /** 10 | * 校验是否为合法的 Cron表达式 11 | */ 12 | public class CronValidator implements ConstraintValidator { 13 | 14 | @Override 15 | public void initialize(IsCron isCron) { 16 | } 17 | 18 | @Override 19 | public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { 20 | try { 21 | return CronExpression.isValidExpression(value); 22 | } catch (Exception e) { 23 | return false; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/common/validator/NotBlankValidator.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.common.validator; 2 | 3 | import cc.mrbird.febs.common.annotation.NotBlank; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import javax.validation.ConstraintValidator; 7 | import javax.validation.ConstraintValidatorContext; 8 | 9 | /** 10 | * @author : wx 11 | * @version : 1 12 | * @date : 2019/9/24 16:22 13 | */ 14 | public class NotBlankValidator implements ConstraintValidator { 15 | @Override 16 | public void initialize(NotBlank constraintAnnotation) { 17 | 18 | } 19 | 20 | @Override 21 | public boolean isValid(String value, ConstraintValidatorContext context) { 22 | return StringUtils.isNotBlank(value); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/config/ScheduleConfig.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 6 | 7 | import javax.sql.DataSource; 8 | import java.util.Properties; 9 | 10 | /** 11 | * 定时任务配置 12 | * 13 | */ 14 | @Configuration 15 | public class ScheduleConfig { 16 | 17 | @Bean 18 | public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) { 19 | SchedulerFactoryBean factory = new SchedulerFactoryBean(); 20 | factory.setDataSource(dataSource); 21 | 22 | // quartz参数 23 | Properties prop = new Properties(); 24 | prop.put("org.quartz.scheduler.instanceName", "MyScheduler"); 25 | prop.put("org.quartz.scheduler.instanceId", "AUTO"); 26 | // 线程池配置 27 | prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); 28 | prop.put("org.quartz.threadPool.threadCount", "20"); 29 | prop.put("org.quartz.threadPool.threadPriority", "5"); 30 | // JobStore配置 31 | prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX"); 32 | // 集群配置 33 | prop.put("org.quartz.jobStore.isClustered", "true"); 34 | prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); 35 | prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); 36 | 37 | prop.put("org.quartz.jobStore.misfireThreshold", "12000"); 38 | prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); 39 | factory.setQuartzProperties(prop); 40 | 41 | factory.setSchedulerName("MyScheduler"); 42 | // 延时启动 43 | factory.setStartupDelay(1); 44 | factory.setApplicationContextSchedulerContextKey("applicationContextKey"); 45 | // 可选,QuartzScheduler 46 | // 启动时更新己存在的 Job 47 | factory.setOverwriteExistingJobs(true); 48 | // 设置自动启动,默认为 true 49 | factory.setAutoStartup(true); 50 | 51 | return factory; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/controller/JobLogController.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.controller; 2 | 3 | import cc.mrbird.febs.common.controller.BaseController; 4 | import cc.mrbird.febs.common.domain.QueryRequest; 5 | import cc.mrbird.febs.common.exception.FebsException; 6 | import cc.mrbird.febs.job.domain.JobLog; 7 | import cc.mrbird.febs.job.service.JobLogService; 8 | import com.baomidou.mybatisplus.core.toolkit.StringPool; 9 | import com.wuwenze.poi.ExcelKit; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.shiro.authz.annotation.RequiresPermissions; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.validation.annotation.Validated; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.servlet.http.HttpServletResponse; 17 | import javax.validation.constraints.NotBlank; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | @Slf4j 22 | @Validated 23 | @RestController 24 | @RequestMapping("job/log") 25 | public class JobLogController extends BaseController { 26 | 27 | private String message; 28 | 29 | @Autowired 30 | private JobLogService jobLogService; 31 | 32 | @GetMapping 33 | @RequiresPermissions("jobLog:view") 34 | public Map jobLogList(QueryRequest request, JobLog log) { 35 | return getDataTable(this.jobLogService.findJobLogs(request, log)); 36 | } 37 | 38 | @DeleteMapping("/{jobIds}") 39 | @RequiresPermissions("jobLog:delete") 40 | public void deleteJobLog(@NotBlank(message = "{required}") @PathVariable String jobIds) throws FebsException { 41 | try { 42 | String[] ids = jobIds.split(StringPool.COMMA); 43 | this.jobLogService.deleteJobLogs(ids); 44 | } catch (Exception e) { 45 | message = "删除调度日志失败"; 46 | log.error(message, e); 47 | throw new FebsException(message); 48 | } 49 | } 50 | 51 | @PostMapping("excel") 52 | @RequiresPermissions("jobLog:export") 53 | public void export(QueryRequest request, JobLog jobLog, HttpServletResponse response) throws FebsException { 54 | try { 55 | List jobLogs = this.jobLogService.findJobLogs(request, jobLog).getRecords(); 56 | ExcelKit.$Export(JobLog.class, response).downXlsx(jobLogs, false); 57 | } catch (Exception e) { 58 | message = "导出Excel失败"; 59 | log.error(message, e); 60 | throw new FebsException(message); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/dao/JobLogMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.dao; 2 | 3 | 4 | import cc.mrbird.febs.job.domain.JobLog; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | 7 | public interface JobLogMapper extends BaseMapper { 8 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/dao/JobMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.dao; 2 | 3 | 4 | import cc.mrbird.febs.job.domain.Job; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | 7 | import java.util.List; 8 | 9 | public interface JobMapper extends BaseMapper { 10 | 11 | List queryList(); 12 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/domain/Job.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.domain; 2 | 3 | import cc.mrbird.febs.common.annotation.IsCron; 4 | import cc.mrbird.febs.common.converter.TimeConverter; 5 | import com.baomidou.mybatisplus.annotation.IdType; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import com.wuwenze.poi.annotation.Excel; 9 | import com.wuwenze.poi.annotation.ExcelField; 10 | import lombok.Data; 11 | import lombok.ToString; 12 | 13 | import javax.validation.constraints.NotBlank; 14 | import javax.validation.constraints.Size; 15 | import java.io.Serializable; 16 | import java.util.Date; 17 | 18 | @Data 19 | @TableName("t_job") 20 | @Excel("定时任务信息表") 21 | public class Job implements Serializable { 22 | 23 | private static final long serialVersionUID = 400066840871805700L; 24 | 25 | /** 26 | * 任务调度参数 key 27 | */ 28 | public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY"; 29 | 30 | public enum ScheduleStatus { 31 | /** 32 | * 正常 33 | */ 34 | NORMAL("0"), 35 | /** 36 | * 暂停 37 | */ 38 | PAUSE("1"); 39 | 40 | private String value; 41 | 42 | ScheduleStatus(String value) { 43 | this.value = value; 44 | } 45 | 46 | public String getValue() { 47 | return value; 48 | } 49 | } 50 | 51 | @TableId(value = "JOB_ID", type = IdType.AUTO) 52 | private Long jobId; 53 | 54 | @NotBlank(message = "{required}") 55 | @Size(max = 50, message = "{noMoreThan}") 56 | @ExcelField(value = "Bean名称") 57 | private String beanName; 58 | 59 | @NotBlank(message = "{required}") 60 | @Size(max = 50, message = "{noMoreThan}") 61 | @ExcelField(value = "方法名称") 62 | private String methodName; 63 | 64 | @Size(max = 50, message = "{noMoreThan}") 65 | @ExcelField(value = "方法参数") 66 | private String params; 67 | 68 | @NotBlank(message = "{required}") 69 | @IsCron(message = "{invalid}") 70 | @ExcelField(value = "Cron表达式") 71 | private String cronExpression; 72 | 73 | @ExcelField(value = "状态", writeConverterExp = "0=正常,1=暂停") 74 | private String status; 75 | 76 | @Size(max = 100, message = "{noMoreThan}") 77 | @ExcelField(value = "备注") 78 | private String remark; 79 | 80 | @ExcelField(value = "创建时间", writeConverter = TimeConverter.class) 81 | private Date createTime; 82 | 83 | private transient String createTimeFrom; 84 | private transient String createTimeTo; 85 | 86 | } 87 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/domain/JobLog.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.domain; 2 | 3 | 4 | import cc.mrbird.febs.common.converter.TimeConverter; 5 | import com.baomidou.mybatisplus.annotation.IdType; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import com.wuwenze.poi.annotation.Excel; 9 | import com.wuwenze.poi.annotation.ExcelField; 10 | import lombok.Data; 11 | import lombok.ToString; 12 | 13 | import java.io.Serializable; 14 | import java.util.Date; 15 | 16 | @Data 17 | @TableName("t_job_log") 18 | @Excel("调度日志信息表") 19 | public class JobLog implements Serializable { 20 | 21 | private static final long serialVersionUID = -7114915445674333148L; 22 | // 任务执行成功 23 | public static final String JOB_SUCCESS = "0"; 24 | // 任务执行失败 25 | public static final String JOB_FAIL = "1"; 26 | 27 | @TableId(value = "LOG_ID", type = IdType.AUTO) 28 | private Long logId; 29 | 30 | private Long jobId; 31 | 32 | @ExcelField(value = "Bean名称") 33 | private String beanName; 34 | 35 | @ExcelField(value = "方法名称") 36 | private String methodName; 37 | 38 | @ExcelField(value = "方法参数") 39 | private String params; 40 | 41 | @ExcelField(value = "状态", writeConverterExp = "0=成功,1=失败") 42 | private String status; 43 | 44 | @ExcelField(value = "异常信息") 45 | private String error; 46 | 47 | @ExcelField(value = "耗时(毫秒)") 48 | private Long times; 49 | 50 | @ExcelField(value = "执行时间", writeConverter = TimeConverter.class) 51 | private Date createTime; 52 | 53 | private transient String createTimeFrom; 54 | private transient String createTimeTo; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/service/JobLogService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.service; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.job.domain.JobLog; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | 9 | public interface JobLogService extends IService { 10 | 11 | IPage findJobLogs(QueryRequest request, JobLog jobLog); 12 | 13 | void saveJobLog(JobLog log); 14 | 15 | void deleteJobLogs(String[] jobLogIds); 16 | } 17 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/service/JobService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.service; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.job.domain.Job; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | 9 | public interface JobService extends IService { 10 | 11 | Job findJob(Long jobId); 12 | 13 | IPage findJobs(QueryRequest request, Job job); 14 | 15 | void createJob(Job job); 16 | 17 | void updateJob(Job job); 18 | 19 | void deleteJobs(String[] jobIds); 20 | 21 | int updateBatch(String jobIds, String status); 22 | 23 | void run(String jobIds); 24 | 25 | void pause(String jobIds); 26 | 27 | void resume(String jobIds); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/task/TestTask.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.task; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Slf4j 7 | @Component 8 | public class TestTask { 9 | 10 | public void test(String params) { 11 | log.info("我是带参数的test方法,正在被执行,参数为:{}" , params); 12 | } 13 | public void test1() { 14 | log.info("我是不带参数的test1方法,正在被执行"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/util/ScheduleJob.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.util; 2 | 3 | import cc.mrbird.febs.common.utils.SpringContextUtil; 4 | import cc.mrbird.febs.job.domain.Job; 5 | import cc.mrbird.febs.job.domain.JobLog; 6 | import cc.mrbird.febs.job.service.JobLogService; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.quartz.JobExecutionContext; 10 | import org.springframework.scheduling.quartz.QuartzJobBean; 11 | 12 | import java.util.Date; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.concurrent.Future; 16 | 17 | /** 18 | * 定时任务 19 | */ 20 | @Slf4j 21 | public class ScheduleJob extends QuartzJobBean { 22 | 23 | private ExecutorService service = Executors.newSingleThreadExecutor(); 24 | 25 | @Override 26 | protected void executeInternal(JobExecutionContext context) { 27 | Job scheduleJob = (Job) context.getMergedJobDataMap().get(Job.JOB_PARAM_KEY); 28 | 29 | // 获取spring bean 30 | JobLogService scheduleJobLogService = SpringContextUtil.getBean(JobLogService.class); 31 | 32 | JobLog jobLog = new JobLog(); 33 | jobLog.setJobId(scheduleJob.getJobId()); 34 | jobLog.setBeanName(scheduleJob.getBeanName()); 35 | jobLog.setMethodName(scheduleJob.getMethodName()); 36 | jobLog.setParams(scheduleJob.getParams()); 37 | jobLog.setCreateTime(new Date()); 38 | 39 | long startTime = System.currentTimeMillis(); 40 | 41 | try { 42 | // 执行任务 43 | log.info("任务准备执行,任务ID:{}", scheduleJob.getJobId()); 44 | ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getBeanName(), scheduleJob.getMethodName(), 45 | scheduleJob.getParams()); 46 | Future future = service.submit(task); 47 | future.get(); 48 | long times = System.currentTimeMillis() - startTime; 49 | jobLog.setTimes(times); 50 | // 任务状态 0:成功 1:失败 51 | jobLog.setStatus(JobLog.JOB_SUCCESS); 52 | 53 | log.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getJobId(), times); 54 | } catch (Exception e) { 55 | log.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e); 56 | long times = System.currentTimeMillis() - startTime; 57 | jobLog.setTimes(times); 58 | // 任务状态 0:成功 1:失败 59 | jobLog.setStatus(JobLog.JOB_FAIL); 60 | jobLog.setError(StringUtils.substring(e.toString(), 0, 2000)); 61 | } finally { 62 | scheduleJobLogService.saveJobLog(jobLog); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/job/util/ScheduleRunnable.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.job.util; 2 | 3 | import cc.mrbird.febs.common.utils.SpringContextUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.util.ReflectionUtils; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * 执行定时任务 12 | */ 13 | @Slf4j 14 | public class ScheduleRunnable implements Runnable { 15 | 16 | private Object target; 17 | private Method method; 18 | private String params; 19 | 20 | ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException { 21 | this.target = SpringContextUtil.getBean(beanName); 22 | this.params = params; 23 | 24 | if (StringUtils.isNotBlank(params)) { 25 | this.method = target.getClass().getDeclaredMethod(methodName, String.class); 26 | } else { 27 | this.method = target.getClass().getDeclaredMethod(methodName); 28 | } 29 | } 30 | 31 | @Override 32 | public void run() { 33 | try { 34 | ReflectionUtils.makeAccessible(method); 35 | if (StringUtils.isNotBlank(params)) { 36 | method.invoke(target, params); 37 | } else { 38 | method.invoke(target); 39 | } 40 | } catch (Exception e) { 41 | log.error("执行定时任务失败", e); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/controller/LogController.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.controller; 2 | 3 | import cc.mrbird.febs.common.annotation.Log; 4 | import cc.mrbird.febs.common.controller.BaseController; 5 | import cc.mrbird.febs.common.domain.QueryRequest; 6 | import cc.mrbird.febs.common.exception.FebsException; 7 | import cc.mrbird.febs.system.domain.SysLog; 8 | import cc.mrbird.febs.system.service.LogService; 9 | import com.baomidou.mybatisplus.core.toolkit.StringPool; 10 | import com.wuwenze.poi.ExcelKit; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.shiro.authz.annotation.RequiresPermissions; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.validation.annotation.Validated; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import javax.servlet.http.HttpServletResponse; 18 | import javax.validation.constraints.NotBlank; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * @author wx 24 | */ 25 | @Slf4j 26 | @Validated 27 | @RestController 28 | @RequestMapping("log") 29 | public class LogController extends BaseController { 30 | 31 | private String message; 32 | 33 | @Autowired 34 | private LogService logService; 35 | 36 | @GetMapping 37 | @RequiresPermissions("log:view") 38 | public Map logList(QueryRequest request, SysLog sysLog) { 39 | return getDataTable(logService.findLogs(request, sysLog)); 40 | } 41 | 42 | @Log("删除系统日志") 43 | @DeleteMapping("/{ids}") 44 | @RequiresPermissions("log:delete") 45 | public void deleteLogss(@NotBlank(message = "{required}") @PathVariable String ids) throws FebsException { 46 | try { 47 | String[] logIds = ids.split(StringPool.COMMA); 48 | this.logService.deleteLogs(logIds); 49 | } catch (Exception e) { 50 | message = "删除日志失败"; 51 | log.error(message, e); 52 | throw new FebsException(message); 53 | } 54 | } 55 | 56 | @PostMapping("excel") 57 | @RequiresPermissions("log:export") 58 | public void export(QueryRequest request, SysLog sysLog, HttpServletResponse response) throws FebsException { 59 | try { 60 | if(request.getPageSize()> exportMaxCount) { 61 | request.setPageSize(exportMaxCount); 62 | } 63 | List sysLogs = this.logService.findLogs(request, sysLog).getRecords(); 64 | ExcelKit.$Export(SysLog.class, response).downXlsx(sysLogs, false); 65 | } catch (Exception e) { 66 | message = "导出Excel失败"; 67 | log.error(message, e); 68 | throw new FebsException(message); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/controller/RedisController.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.controller; 2 | 3 | import cc.mrbird.febs.common.domain.FebsResponse; 4 | import cc.mrbird.febs.common.domain.RedisInfo; 5 | import cc.mrbird.febs.common.service.RedisService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | @RestController 15 | @RequestMapping("redis") 16 | public class RedisController { 17 | 18 | @Autowired 19 | private RedisService redisService; 20 | 21 | @GetMapping("info") 22 | public FebsResponse getRedisInfo() throws Exception { 23 | List infoList = this.redisService.getRedisInfo(); 24 | return new FebsResponse().data(infoList); 25 | } 26 | 27 | @GetMapping("keysSize") 28 | public Map getKeysSize() throws Exception { 29 | return redisService.getKeysSize(); 30 | } 31 | 32 | @GetMapping("memoryInfo") 33 | public Map getMemoryInfo() throws Exception { 34 | return redisService.getMemoryInfo(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/CityMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.common.annotation.DataFilter; 4 | import cc.mrbird.febs.system.domain.City; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | @DataFilter(filterMethods={"selectPage"}) 7 | public interface CityMapper extends BaseMapper { 8 | } 9 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/DeptMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.Dept; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | public interface DeptMapper extends BaseMapper { 7 | 8 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/DictMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.Dict; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | public interface DictMapper extends BaseMapper { 7 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/LogMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.SysLog; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | public interface LogMapper extends BaseMapper { 7 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/LoginLogMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | 4 | import cc.mrbird.febs.system.domain.LoginLog; 5 | import cc.mrbird.febs.system.domain.User; 6 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public interface LoginLogMapper extends BaseMapper { 12 | 13 | /** 14 | * 获取系统总访问次数 15 | * 16 | * @return Long 17 | */ 18 | Long findTotalVisitCount(); 19 | 20 | /** 21 | * 获取系统今日访问次数 22 | * 23 | * @return Long 24 | */ 25 | Long findTodayVisitCount(); 26 | 27 | /** 28 | * 获取系统今日访问 IP数 29 | * 30 | * @return Long 31 | */ 32 | Long findTodayIp(); 33 | 34 | /** 35 | * 获取系统近七天来的访问记录 36 | * 37 | * @param user 用户 38 | * @return 系统近七天来的访问记录 39 | */ 40 | List> findLastSevenDaysVisitCount(User user); 41 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/MenuMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.Menu; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | import java.util.List; 7 | 8 | public interface MenuMapper extends BaseMapper { 9 | 10 | List findUserPermissions(String userName); 11 | 12 | List findUserMenus(String userName); 13 | 14 | /** 15 | * 查找当前菜单/按钮关联的用户 ID 16 | * 17 | * @param menuId menuId 18 | * @return 用户 ID集合 19 | */ 20 | List findUserIdsByMenuId(String menuId); 21 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/RoleMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.Role; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | import java.util.List; 7 | 8 | public interface RoleMapper extends BaseMapper { 9 | 10 | List findUserRole(String userName); 11 | 12 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/RoleMenuMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.RoleMenu; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | public interface RoleMenuMapper extends BaseMapper { 7 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/TestMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.Test; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | public interface TestMapper extends BaseMapper { 6 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/UserConfigMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.UserConfig; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | public interface UserConfigMapper extends BaseMapper { 7 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/UserMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.DeptUsers; 4 | import cc.mrbird.febs.system.domain.User; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | import com.baomidou.mybatisplus.core.metadata.IPage; 7 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 8 | import org.apache.ibatis.annotations.Param; 9 | 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public interface UserMapper extends BaseMapper { 14 | 15 | IPage findUserDetail(Page page, @Param("user") User user); 16 | 17 | /** 18 | * 获取单个用户详情 19 | * 20 | * @param username 用户名 21 | * @return 用户信息 22 | */ 23 | User findDetail(String username); 24 | String findSubordinates(@Param("deptId")Long deptId); 25 | 26 | List findSubordinatesMap(); 27 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/dao/UserRoleMapper.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.dao; 2 | 3 | import cc.mrbird.febs.system.domain.UserRole; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | public interface UserRoleMapper extends BaseMapper { 8 | 9 | /** 10 | * 根据用户Id删除该用户的角色关系 11 | * 12 | * @param userId 用户ID 13 | * @return boolean 14 | * @author lzx 15 | * @date 2019年03月04日17:46:49 16 | */ 17 | Boolean deleteByUserId(@Param("userId") Long userId); 18 | 19 | /** 20 | * 根据角色Id删除该角色的用户关系 21 | * 22 | * @param roleId 角色ID 23 | * @return boolean 24 | * @author lzx 25 | * @date 2019年03月04日17:47:16 26 | */ 27 | Boolean deleteByRoleId(@Param("roleId") Long roleId); 28 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/City.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import cc.mrbird.febs.common.converter.TimeConverter; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import com.wuwenze.poi.annotation.Excel; 8 | import com.wuwenze.poi.annotation.ExcelField; 9 | import lombok.Data; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import java.io.Serializable; 13 | import java.util.Date; 14 | 15 | @Data 16 | @TableName("city") 17 | @Excel("城市信息表") 18 | public class City implements Serializable { 19 | private static final long serialVersionUID = -4852732617765810959L; 20 | 21 | @TableId(value = "CITY_ID", type = IdType.AUTO) 22 | private Long cityId; 23 | /** 24 | * 城市名称 25 | */ 26 | @ExcelField(value = "城市名称", required = true, maxLength = 20, 27 | comment = "提示:必填,长度不能超过20个字符") 28 | @NotBlank(message = "{required}") 29 | private String cityName; 30 | /** 31 | * 城市简介 32 | */ 33 | @ExcelField(value = "城市简介",maxLength = 255, 34 | comment = "提示:长度不能超过255个字符") 35 | private String introduce; 36 | /** 37 | * 经度 38 | */ 39 | @ExcelField(value = "经度",maxLength = 11, regularExp = "[0-9]+.*[0-9]*", 40 | regularExpMessage = "必须是数字", comment = "提示: 只能填写数字,并且长度不能超过11位") 41 | private String longitude; 42 | /** 43 | * 纬度 44 | */ 45 | @ExcelField(value = "纬度",maxLength = 11, regularExp = "[0-9]+.*[0-9]*", 46 | regularExpMessage = "必须是数字", comment = "提示: 只能填写数字,并且长度不能超过11位") 47 | private String latitude; 48 | 49 | 50 | private Date createTime; 51 | 52 | private Long creator; 53 | } 54 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/Dept.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import cc.mrbird.febs.common.converter.TimeConverter; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import com.wuwenze.poi.annotation.Excel; 8 | import com.wuwenze.poi.annotation.ExcelField; 9 | import lombok.Data; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import javax.validation.constraints.Size; 13 | import java.io.Serializable; 14 | import java.util.Date; 15 | 16 | @Data 17 | @TableName("t_dept") 18 | @Excel("部门信息表") 19 | public class Dept implements Serializable { 20 | 21 | private static final long serialVersionUID = -7790334862410409053L; 22 | 23 | @TableId(value = "DEPT_ID", type = IdType.AUTO) 24 | private Long deptId; 25 | 26 | private Long parentId; 27 | 28 | @NotBlank(message = "{required}") 29 | @Size(max = 20, message = "{noMoreThan}") 30 | @ExcelField(value = "部门名称") 31 | private String deptName; 32 | 33 | private Double orderNum; 34 | 35 | @ExcelField(value = "创建时间", writeConverter = TimeConverter.class) 36 | private Date createTime; 37 | 38 | @ExcelField(value = "修改时间", writeConverter = TimeConverter.class) 39 | private Date modifyTime; 40 | 41 | private transient String createTimeFrom; 42 | 43 | private transient String createTimeTo; 44 | 45 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/DeptUsers.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | @Data 7 | public class DeptUsers implements Serializable { 8 | private static final long serialVersionUID = -5615190273698747063L; 9 | private Long deptId; 10 | private String userIds; 11 | } 12 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/Dict.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import com.wuwenze.poi.annotation.Excel; 7 | import com.wuwenze.poi.annotation.ExcelField; 8 | import lombok.Data; 9 | import lombok.ToString; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import javax.validation.constraints.Size; 13 | import java.io.Serializable; 14 | 15 | @Data 16 | @TableName("t_dict") 17 | @Excel("字典信息表") 18 | public class Dict implements Serializable { 19 | 20 | private static final long serialVersionUID = 7780820231535870010L; 21 | 22 | @TableId(value = "DICT_ID", type = IdType.AUTO) 23 | private Long dictId; 24 | 25 | @NotBlank(message = "{required}") 26 | @Size(max = 10, message = "{noMoreThan}") 27 | @ExcelField(value = "键") 28 | private String keyy; 29 | 30 | @NotBlank(message = "{required}") 31 | @Size(max = 20, message = "{noMoreThan}") 32 | @ExcelField(value = "值") 33 | private String valuee; 34 | 35 | @NotBlank(message = "{required}") 36 | @Size(max = 20, message = "{noMoreThan}") 37 | @ExcelField(value = "表名") 38 | private String tableName; 39 | 40 | @NotBlank(message = "{required}") 41 | @Size(max = 20, message = "{noMoreThan}") 42 | @ExcelField(value = "字段名") 43 | private String fieldName; 44 | 45 | @Size(max = 20, message = "{noMoreThan}") 46 | @ExcelField(value = "字段名") 47 | private String otherKeyy; 48 | } 49 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/LoginLog.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | import lombok.ToString; 6 | 7 | import java.util.Date; 8 | 9 | @TableName("t_login_log") 10 | @Data 11 | public class LoginLog { 12 | /** 13 | * 用户 ID 14 | */ 15 | private String username; 16 | 17 | /** 18 | * 登录时间 19 | */ 20 | private Date loginTime; 21 | 22 | /** 23 | * 登录地点 24 | */ 25 | private String location; 26 | 27 | private String ip; 28 | } 29 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/Menu.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import cc.mrbird.febs.common.converter.TimeConverter; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import com.wuwenze.poi.annotation.Excel; 8 | import com.wuwenze.poi.annotation.ExcelField; 9 | import lombok.Data; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import javax.validation.constraints.Size; 13 | import java.io.Serializable; 14 | import java.util.Date; 15 | 16 | @Data 17 | @TableName("t_menu") 18 | @Excel("菜单信息表") 19 | public class Menu implements Serializable { 20 | 21 | private static final long serialVersionUID = 7187628714679791771L; 22 | 23 | public static final String TYPE_MENU = "0"; 24 | 25 | public static final String TYPE_BUTTON = "1"; 26 | 27 | @TableId(value = "MENU_ID", type = IdType.AUTO) 28 | private Long menuId; 29 | 30 | private Long parentId; 31 | 32 | @NotBlank(message = "{required}") 33 | @Size(max = 10, message = "{noMoreThan}") 34 | @ExcelField(value = "名称") 35 | private String menuName; 36 | 37 | @Size(max = 50, message = "{noMoreThan}") 38 | @ExcelField(value = "地址") 39 | private String path; 40 | 41 | @Size(max = 100, message = "{noMoreThan}") 42 | @ExcelField(value = "对应Vue组件") 43 | private String component; 44 | 45 | @Size(max = 50, message = "{noMoreThan}") 46 | @ExcelField(value = "权限") 47 | private String perms; 48 | 49 | @ExcelField(value = "图标") 50 | private String icon; 51 | 52 | @NotBlank(message = "{required}") 53 | @ExcelField(value = "类型", writeConverterExp = "0=按钮,1=菜单") 54 | private String type; 55 | 56 | private Double orderNum; 57 | 58 | @ExcelField(value = "创建时间", writeConverter = TimeConverter.class) 59 | private Date createTime; 60 | 61 | @ExcelField(value = "修改时间", writeConverter = TimeConverter.class) 62 | private Date modifyTime; 63 | 64 | private transient String createTimeFrom; 65 | private transient String createTimeTo; 66 | 67 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/Role.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import cc.mrbird.febs.common.converter.DataScopeReadConverter; 4 | import cc.mrbird.febs.common.converter.DataScopeWriteConverter; 5 | import cc.mrbird.febs.common.converter.TimeConverter; 6 | import cc.mrbird.febs.common.options.DataScopeOptions; 7 | import com.baomidou.mybatisplus.annotation.IdType; 8 | import com.baomidou.mybatisplus.annotation.TableId; 9 | import com.baomidou.mybatisplus.annotation.TableName; 10 | import com.wuwenze.poi.annotation.Excel; 11 | import com.wuwenze.poi.annotation.ExcelField; 12 | import lombok.Data; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | import javax.validation.constraints.NotNull; 16 | import javax.validation.constraints.Size; 17 | import java.io.Serializable; 18 | import java.util.Date; 19 | 20 | @Data 21 | @TableName("t_role") 22 | @Excel("角色信息表") 23 | public class Role implements Serializable { 24 | 25 | private static final long serialVersionUID = -1714476694755654924L; 26 | 27 | @TableId(value = "ROLE_ID", type = IdType.AUTO) 28 | private Long roleId; 29 | 30 | @NotBlank(message = "{required}") 31 | @Size(max = 10, message = "{noMoreThan}") 32 | @ExcelField(value = "角色名称") 33 | private String roleName; 34 | 35 | @Size(max = 50, message = "{noMoreThan}") 36 | @ExcelField(value = "角色描述") 37 | private String remark; 38 | 39 | @ExcelField(value = "创建时间", writeConverter = TimeConverter.class) 40 | private Date createTime; 41 | 42 | @ExcelField(value = "修改时间", writeConverter = TimeConverter.class) 43 | private Date modifyTime; 44 | 45 | private transient String createTimeFrom; 46 | private transient String createTimeTo; 47 | private transient String menuId; 48 | @NotNull(message = "{required}") 49 | @ExcelField(value = "数据范围", writeConverter = DataScopeWriteConverter.class,readConverter = DataScopeReadConverter.class,options = DataScopeOptions.class ) 50 | private Integer dataScope; 51 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/RoleMenu.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @TableName("t_role_menu") 9 | @Data 10 | public class RoleMenu implements Serializable { 11 | 12 | private static final long serialVersionUID = -7573904024872252113L; 13 | 14 | private Long roleId; 15 | 16 | private Long menuId; 17 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/SysLog.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import cc.mrbird.febs.common.converter.TimeConverter; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import com.wuwenze.poi.annotation.Excel; 8 | import com.wuwenze.poi.annotation.ExcelField; 9 | import lombok.Data; 10 | 11 | import java.io.Serializable; 12 | import java.util.Date; 13 | 14 | @Data 15 | @TableName("t_log") 16 | @Excel("系统日志表") 17 | public class SysLog implements Serializable { 18 | 19 | private static final long serialVersionUID = -8878596941954995444L; 20 | 21 | @TableId(value = "ID", type = IdType.AUTO) 22 | private Long id; 23 | 24 | @ExcelField(value = "操作人") 25 | private String username; 26 | 27 | @ExcelField(value = "操作描述") 28 | private String operation; 29 | 30 | @ExcelField(value = "耗时(毫秒)") 31 | private Long time; 32 | 33 | @ExcelField(value = "执行方法") 34 | private String method; 35 | 36 | @ExcelField(value = "方法参数") 37 | private String params; 38 | 39 | @ExcelField(value = "IP地址") 40 | private String ip; 41 | 42 | @ExcelField(value = "操作时间", writeConverter = TimeConverter.class) 43 | private Date createTime; 44 | 45 | private transient String createTimeFrom; 46 | private transient String createTimeTo; 47 | 48 | @ExcelField(value = "操作地点") 49 | private String location; 50 | 51 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/Test.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import com.wuwenze.poi.annotation.Excel; 5 | import com.wuwenze.poi.annotation.ExcelField; 6 | import com.wuwenze.poi.validator.EmailValidator; 7 | import lombok.Data; 8 | 9 | import java.util.Date; 10 | 11 | @Data 12 | @TableName("t_test") 13 | @Excel("测试导入导出数据") 14 | public class Test { 15 | private Long id; 16 | @ExcelField(value = "字段1", required = true, maxLength = 20, 17 | comment = "提示:必填,长度不能超过20个字符") 18 | private String field1; 19 | 20 | @ExcelField(value = "字段2", required = true, maxLength = 11, regularExp = "[0-9]+", 21 | regularExpMessage = "必须是数字", comment = "提示: 必填,只能填写数字,并且长度不能超过11位") 22 | private Integer field2; 23 | 24 | @ExcelField(value = "字段3", required = true, maxLength = 50, 25 | comment = "提示:必填,只能填写邮箱,长度不能超过50个字符", validator = EmailValidator.class) 26 | private String field3; 27 | 28 | private Date createTime; 29 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/UserConfig.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableId; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import lombok.Data; 6 | 7 | 8 | @TableName("t_user_config") 9 | @Data 10 | public class UserConfig { 11 | 12 | public static final String DEFAULT_THEME = "dark"; 13 | public static final String DEFAULT_LAYOUT = "side"; 14 | public static final String DEFAULT_MULTIPAGE = "0"; 15 | public static final String DEFAULT_FIX_SIDERBAR = "1"; 16 | public static final String DEFAULT_FIX_HEADER = "1"; 17 | public static final String DEFAULT_COLOR = "rgb(66, 185, 131)"; 18 | 19 | /** 20 | * 用户 ID 21 | */ 22 | @TableId(value = "USER_ID") 23 | private Long userId; 24 | 25 | /** 26 | * 系统主题 dark暗色风格,light明亮风格 27 | */ 28 | private String theme; 29 | 30 | /** 31 | * 系统布局 side侧边栏,head顶部栏 32 | */ 33 | private String layout; 34 | 35 | /** 36 | * 页面风格 1多标签页 0单页 37 | */ 38 | private String multiPage; 39 | 40 | /** 41 | * 页面滚动是否固定侧边栏 1固定 0不固定 42 | */ 43 | private String fixSiderbar; 44 | 45 | /** 46 | * 页面滚动是否固定顶栏 1固定 0不固定 47 | */ 48 | private String fixHeader; 49 | 50 | /** 51 | * 主题颜色 RGB值 52 | */ 53 | private String color; 54 | 55 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/domain/UserRole.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.domain; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @TableName("t_user_role") 9 | @Data 10 | public class UserRole implements Serializable{ 11 | 12 | private static final long serialVersionUID = -3166012934498268403L; 13 | 14 | private Long userId; 15 | 16 | private Long roleId; 17 | 18 | } -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/CityService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.system.domain.City; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | public interface CityService extends IService { 11 | 12 | IPage findCitys(QueryRequest request, City city); 13 | 14 | List findCityList(QueryRequest request, City city); 15 | 16 | List selectListByIds(List idsList); 17 | 18 | void createCity(City city) throws Exception; 19 | 20 | void updateCity(City city); 21 | 22 | void deleteCitys(String[] cityIds); 23 | 24 | /** 25 | * 批量插入 26 | * @param list List 27 | */ 28 | void batchInsert(List list); 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/DeptService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | 4 | import cc.mrbird.febs.common.domain.QueryRequest; 5 | import cc.mrbird.febs.system.domain.Dept; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public interface DeptService extends IService { 12 | 13 | Map findDepts(QueryRequest request, Dept dept); 14 | 15 | List findDepts(Dept dept, QueryRequest request); 16 | 17 | void createDept(Dept dept); 18 | 19 | void updateDept(Dept dept); 20 | 21 | void deleteDepts(String[] deptIds); 22 | } 23 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/DictService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.system.domain.Dict; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | 9 | public interface DictService extends IService { 10 | 11 | IPage findDicts(QueryRequest request, Dict dict); 12 | 13 | void createDict(Dict dict); 14 | 15 | void updateDict(Dict dicdt); 16 | 17 | void deleteDicts(String[] dictIds); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/LogService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.system.domain.SysLog; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | import com.fasterxml.jackson.core.JsonProcessingException; 8 | import org.aspectj.lang.ProceedingJoinPoint; 9 | import org.springframework.scheduling.annotation.Async; 10 | 11 | 12 | public interface LogService extends IService { 13 | 14 | IPage findLogs(QueryRequest request, SysLog sysLog); 15 | 16 | void deleteLogs(String[] logIds); 17 | 18 | @Async 19 | void saveLog(ProceedingJoinPoint point, SysLog log) throws JsonProcessingException; 20 | } 21 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/LoginLogService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.system.domain.LoginLog; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | public interface LoginLogService extends IService { 7 | 8 | void saveLoginLog (LoginLog loginLog); 9 | } 10 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/MenuService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.system.domain.Menu; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public interface MenuService extends IService { 10 | 11 | List findUserPermissions(String username); 12 | 13 | List findUserMenus(String username); 14 | 15 | Map findMenus(Menu menu); 16 | 17 | List findMenuList(Menu menu); 18 | 19 | void createMenu(Menu menu); 20 | 21 | void updateMenu(Menu menu) throws Exception; 22 | 23 | /** 24 | * 递归删除菜单/按钮 25 | * 26 | * @param menuIds menuIds 27 | */ 28 | void deleteMeuns(String[] menuIds) throws Exception; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/RoleMenuServie.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.system.domain.RoleMenu; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import java.util.List; 7 | 8 | public interface RoleMenuServie extends IService { 9 | 10 | void deleteRoleMenusByRoleId(String[] roleIds); 11 | 12 | void deleteRoleMenusByMenuId(String[] menuIds); 13 | 14 | List getRoleMenusByRoleId(String roleId); 15 | } 16 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/RoleService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.system.domain.Role; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | public interface RoleService extends IService { 11 | 12 | IPage findRoles(Role role, QueryRequest request); 13 | 14 | List findUserRole(String userName); 15 | 16 | Role findByName(String roleName); 17 | 18 | void createRole(Role role); 19 | 20 | void deleteRoles(String[] roleIds) throws Exception; 21 | 22 | void updateRole(Role role) throws Exception; 23 | } 24 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/TestService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.system.domain.Test; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import java.util.List; 7 | 8 | public interface TestService extends IService { 9 | 10 | List findTests(); 11 | 12 | /** 13 | * 批量插入 14 | * @param list List 15 | */ 16 | void batchInsert(List list); 17 | } 18 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/UserConfigService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.system.domain.UserConfig; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | public interface UserConfigService extends IService { 7 | 8 | /** 9 | * 通过用户 ID 获取前端系统个性化配置 10 | * 11 | * @param userId 用户 ID 12 | * @return 前端系统个性化配置 13 | */ 14 | UserConfig findByUserId(String userId); 15 | 16 | /** 17 | * 生成用户默认个性化配置 18 | * 19 | * @param userId 用户 ID 20 | */ 21 | void initDefaultUserConfig(String userId); 22 | 23 | /** 24 | * 通过用户 ID 删除个性化配置 25 | * 26 | * @param userIds 用户 ID 数组 27 | */ 28 | void deleteByUserId(String... userIds); 29 | 30 | /** 31 | * 更新用户个性化配置 32 | * 33 | * @param userConfig 用户个性化配置 34 | */ 35 | void update(UserConfig userConfig) throws Exception; 36 | } 37 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/UserRoleService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | 4 | import cc.mrbird.febs.system.domain.UserRole; 5 | import com.baomidou.mybatisplus.extension.service.IService; 6 | 7 | import java.util.List; 8 | 9 | public interface UserRoleService extends IService { 10 | 11 | void deleteUserRolesByRoleId(String[] roleIds); 12 | 13 | void deleteUserRolesByUserId(String[] userIds); 14 | 15 | List findUserIdsByRoleId(String[] roleIds); 16 | } 17 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/UserService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.system.domain.DeptUsers; 5 | import cc.mrbird.febs.system.domain.User; 6 | import com.baomidou.mybatisplus.core.metadata.IPage; 7 | import com.baomidou.mybatisplus.extension.service.IService; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | 13 | public interface UserService extends IService { 14 | 15 | /** 16 | * 通过用户名查找用户 17 | * 18 | * @param username username 19 | * @return user 20 | */ 21 | User findByName(String username); 22 | 23 | /** 24 | * 查询用户详情,包括基本信息,用户角色,用户部门 25 | * 26 | * @param user user 27 | * @param queryRequest queryRequest 28 | * @return IPage 29 | */ 30 | IPage findUserDetail(User user, QueryRequest queryRequest); 31 | 32 | /** 33 | * 更新用户登录时间 34 | * 35 | * @param username username 36 | */ 37 | void updateLoginTime(String username) throws Exception; 38 | 39 | /** 40 | * 新增用户 41 | * 42 | * @param user user 43 | */ 44 | void createUser(User user) throws Exception; 45 | 46 | /** 47 | * 修改用户 48 | * 49 | * @param user user 50 | */ 51 | void updateUser(User user) throws Exception; 52 | 53 | /** 54 | * 删除用户 55 | * 56 | * @param userIds 用户 id数组 57 | */ 58 | void deleteUsers(String[] userIds) throws Exception; 59 | 60 | /** 61 | * 更新个人信息 62 | * 63 | * @param user 个人信息 64 | */ 65 | void updateProfile(User user) throws Exception; 66 | 67 | /** 68 | * 更新用户头像 69 | * 70 | * @param username 用户名 71 | * @param avatar 用户头像 72 | */ 73 | void updateAvatar(String username, String avatar) throws Exception; 74 | 75 | /** 76 | * 更新用户密码 77 | * 78 | * @param username 用户名 79 | * @param password 新密码 80 | */ 81 | void updatePassword(String username, String password) throws Exception; 82 | 83 | /** 84 | * 注册用户 85 | * 86 | * @param username 用户名 87 | * @param password 密码 88 | */ 89 | void regist(String username, String password) throws Exception; 90 | 91 | /** 92 | * 重置密码 93 | * 94 | * @param usernames 用户集合 95 | */ 96 | void resetPassword(String[] usernames) throws Exception; 97 | 98 | String findSubordinates(Long deptId) throws Exception; 99 | 100 | List findSubordinatesMap(); 101 | } 102 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/impl/DictServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service.impl; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.common.utils.SortUtil; 5 | import cc.mrbird.febs.system.dao.DictMapper; 6 | import cc.mrbird.febs.system.domain.Dict; 7 | import cc.mrbird.febs.system.service.DictService; 8 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 9 | import com.baomidou.mybatisplus.core.metadata.IPage; 10 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 11 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.transaction.annotation.Propagation; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | @Slf4j 22 | @Service("dictService") 23 | @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) 24 | public class DictServiceImpl extends ServiceImpl implements DictService { 25 | 26 | @Override 27 | public IPage findDicts(QueryRequest request, Dict dict) { 28 | try { 29 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 30 | 31 | if (StringUtils.isNotBlank(dict.getKeyy())) { 32 | queryWrapper.eq(Dict::getKeyy, dict.getKeyy()); 33 | } 34 | if (StringUtils.isNotBlank(dict.getValuee())) { 35 | queryWrapper.eq(Dict::getValuee, dict.getValuee()); 36 | } 37 | if (StringUtils.isNotBlank(dict.getTableName())) { 38 | queryWrapper.eq(Dict::getTableName, dict.getTableName()); 39 | } 40 | if (StringUtils.isNotBlank(dict.getFieldName())) { 41 | queryWrapper.eq(Dict::getFieldName, dict.getFieldName()); 42 | } 43 | 44 | Page page = new Page<>(); 45 | SortUtil.handlePageSort(request, page, true); 46 | return this.page(page, queryWrapper); 47 | } catch (Exception e) { 48 | log.error("获取字典信息失败", e); 49 | return null; 50 | } 51 | } 52 | 53 | @Override 54 | @Transactional 55 | public void createDict(Dict dict) { 56 | this.save(dict); 57 | } 58 | 59 | @Override 60 | @Transactional 61 | public void updateDict(Dict dict) { 62 | this.baseMapper.updateById(dict); 63 | } 64 | 65 | @Override 66 | @Transactional 67 | public void deleteDicts(String[] dictIds) { 68 | List list = Arrays.asList(dictIds); 69 | this.baseMapper.deleteBatchIds(list); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/impl/LoginLogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service.impl; 2 | 3 | import cc.mrbird.febs.common.utils.AddressUtil; 4 | import cc.mrbird.febs.common.utils.HttpContextUtil; 5 | import cc.mrbird.febs.common.utils.IPUtil; 6 | import cc.mrbird.febs.system.dao.LoginLogMapper; 7 | import cc.mrbird.febs.system.domain.LoginLog; 8 | import cc.mrbird.febs.system.service.LoginLogService; 9 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Propagation; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.util.Date; 16 | 17 | @Service("loginLogService") 18 | @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) 19 | public class LoginLogServiceImpl extends ServiceImpl implements LoginLogService { 20 | 21 | @Override 22 | @Transactional 23 | public void saveLoginLog(LoginLog loginLog) { 24 | loginLog.setLoginTime(new Date()); 25 | HttpServletRequest request = HttpContextUtil.getHttpServletRequest(); 26 | String ip = IPUtil.getIpAddr(request); 27 | loginLog.setIp(ip); 28 | loginLog.setLocation(AddressUtil.getCityInfo(ip)); 29 | this.save(loginLog); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/impl/RoleMenuServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service.impl; 2 | 3 | import cc.mrbird.febs.system.dao.RoleMenuMapper; 4 | import cc.mrbird.febs.system.domain.RoleMenu; 5 | import cc.mrbird.febs.system.service.RoleMenuServie; 6 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Propagation; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | @Service("roleMenuService") 16 | @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) 17 | public class RoleMenuServiceImpl extends ServiceImpl implements RoleMenuServie { 18 | 19 | @Override 20 | @Transactional 21 | public void deleteRoleMenusByRoleId(String[] roleIds) { 22 | List list = Arrays.asList(roleIds); 23 | baseMapper.delete(new LambdaQueryWrapper().in(RoleMenu::getRoleId, list)); 24 | } 25 | 26 | @Override 27 | @Transactional 28 | public void deleteRoleMenusByMenuId(String[] menuIds) { 29 | List list = Arrays.asList(menuIds); 30 | baseMapper.delete(new LambdaQueryWrapper().in(RoleMenu::getMenuId, list)); 31 | } 32 | 33 | @Override 34 | public List getRoleMenusByRoleId(String roleId) { 35 | return baseMapper.selectList(new LambdaQueryWrapper().eq(RoleMenu::getRoleId, roleId)); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/impl/TestServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service.impl; 2 | 3 | import cc.mrbird.febs.system.dao.TestMapper; 4 | import cc.mrbird.febs.system.domain.Test; 5 | import cc.mrbird.febs.system.service.TestService; 6 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Propagation; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | @Slf4j 18 | @Service("testService") 19 | @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) 20 | public class TestServiceImpl extends ServiceImpl implements TestService { 21 | 22 | @Value("${febs.max.batch.insert.num}") 23 | private int batchInsertMaxNum; 24 | 25 | @Override 26 | public List findTests() { 27 | try { 28 | return baseMapper.selectList(new QueryWrapper().orderByDesc("create_time")); 29 | } catch (Exception e) { 30 | log.error("获取信息失败", e); 31 | return new ArrayList<>(); 32 | } 33 | } 34 | 35 | @Override 36 | @Transactional 37 | public void batchInsert(List list) { 38 | int total = list.size(); 39 | int max = batchInsertMaxNum; 40 | int count = total / max; 41 | int left = total % max; 42 | int length; 43 | if (left == 0) length = count; 44 | else length = count + 1; 45 | for (int i = 0; i < length; i++) { 46 | int start = max * i; 47 | int end = max * (i + 1); 48 | if (i != count) { 49 | log.info("正在插入第" + (start + 1) + " ~ " + end + "条记录 ······"); 50 | saveBatch(list, end); 51 | } else { 52 | end = total; 53 | log.info("正在插入第" + (start + 1) + " ~ " + end + "条记录 ······"); 54 | saveBatch(list, end); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/impl/UserConfigServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service.impl; 2 | 3 | import cc.mrbird.febs.common.service.CacheService; 4 | import cc.mrbird.febs.system.dao.UserConfigMapper; 5 | import cc.mrbird.febs.system.domain.UserConfig; 6 | import cc.mrbird.febs.system.service.UserConfigService; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Propagation; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | @Service("userConfigService") 17 | @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) 18 | public class UserConfigServiceImpl extends ServiceImpl implements UserConfigService { 19 | 20 | @Autowired 21 | private CacheService cacheService; 22 | 23 | @Override 24 | public UserConfig findByUserId(String userId) { 25 | return baseMapper.selectById(userId); 26 | } 27 | 28 | @Override 29 | @Transactional 30 | public void initDefaultUserConfig(String userId) { 31 | UserConfig userConfig = new UserConfig(); 32 | userConfig.setUserId(Long.valueOf(userId)); 33 | userConfig.setColor(UserConfig.DEFAULT_COLOR); 34 | userConfig.setFixHeader(UserConfig.DEFAULT_FIX_HEADER); 35 | userConfig.setFixSiderbar(UserConfig.DEFAULT_FIX_SIDERBAR); 36 | userConfig.setLayout(UserConfig.DEFAULT_LAYOUT); 37 | userConfig.setTheme(UserConfig.DEFAULT_THEME); 38 | userConfig.setMultiPage(UserConfig.DEFAULT_MULTIPAGE); 39 | baseMapper.insert(userConfig); 40 | } 41 | 42 | @Override 43 | @Transactional 44 | public void deleteByUserId(String... userIds) { 45 | List list = Arrays.asList(userIds); 46 | baseMapper.deleteBatchIds(list); 47 | } 48 | 49 | @Override 50 | @Transactional 51 | public void update(UserConfig userConfig) throws Exception { 52 | baseMapper.updateById(userConfig); 53 | cacheService.saveUserConfigs(String.valueOf(userConfig.getUserId())); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/system/service/impl/UserRoleServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.system.service.impl; 2 | 3 | import cc.mrbird.febs.system.dao.UserRoleMapper; 4 | import cc.mrbird.febs.system.domain.UserRole; 5 | import cc.mrbird.febs.system.service.UserRoleService; 6 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Propagation; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | @Service("userRoleService") 17 | @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) 18 | public class UserRoleServiceImpl extends ServiceImpl implements UserRoleService { 19 | 20 | @Override 21 | @Transactional 22 | public void deleteUserRolesByRoleId(String[] roleIds) { 23 | Arrays.stream(roleIds).forEach(id -> baseMapper.deleteByRoleId(Long.valueOf(id))); 24 | } 25 | 26 | @Override 27 | @Transactional 28 | public void deleteUserRolesByUserId(String[] userIds) { 29 | Arrays.stream(userIds).forEach(id -> baseMapper.deleteByUserId(Long.valueOf(id))); 30 | } 31 | 32 | @Override 33 | public List findUserIdsByRoleId(String[] roleIds) { 34 | 35 | List list = baseMapper.selectList(new LambdaQueryWrapper().in(UserRole::getRoleId, String.join(",", roleIds))); 36 | return list.stream().map(userRole -> String.valueOf(userRole.getUserId())).collect(Collectors.toList()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/web/controller/ArticleController.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.web.controller; 2 | 3 | import cc.mrbird.febs.common.domain.FebsConstant; 4 | import cc.mrbird.febs.common.domain.FebsResponse; 5 | import cc.mrbird.febs.common.exception.FebsException; 6 | import cc.mrbird.febs.common.utils.HttpUtil; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.apache.shiro.authz.annotation.RequiresPermissions; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @Slf4j 15 | @RestController 16 | @RequestMapping("article") 17 | public class ArticleController { 18 | 19 | @GetMapping 20 | @RequiresPermissions("article:view") 21 | public FebsResponse queryArticle(String date) throws FebsException { 22 | String param; 23 | String data; 24 | try { 25 | if (StringUtils.isNotBlank(date)) { 26 | param = "dev=1&date=" + date; 27 | data = HttpUtil.sendSSLPost(FebsConstant.MRYW_DAY_URL, param); 28 | } else { 29 | param = "dev=1"; 30 | data = HttpUtil.sendSSLPost(FebsConstant.MRYW_TODAY_URL, param); 31 | } 32 | return new FebsResponse().data(data).code("200").message("获取文章成功").status("success"); 33 | } catch (Exception e) { 34 | String message = "获取文章失败"; 35 | log.error(message, e); 36 | throw new FebsException(message); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/web/controller/CityService.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.web.controller; 2 | 3 | import cc.mrbird.febs.common.domain.QueryRequest; 4 | import cc.mrbird.febs.system.domain.City; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | public interface CityService extends IService { 11 | 12 | IPage findCitys(QueryRequest request, City city); 13 | 14 | List findCityList(QueryRequest request, City city); 15 | 16 | List selectListByIds(List idsList); 17 | 18 | void createCity(City city) throws Exception; 19 | 20 | void updateCity(City city); 21 | 22 | void deleteCitys(String[] cityIds); 23 | 24 | /** 25 | * 批量插入 26 | * @param list List 27 | */ 28 | void batchInsert(List list); 29 | } 30 | -------------------------------------------------------------------------------- /backend/src/main/java/cc/mrbird/febs/web/controller/WeatherController.java: -------------------------------------------------------------------------------- 1 | package cc.mrbird.febs.web.controller; 2 | 3 | import cc.mrbird.febs.common.domain.FebsConstant; 4 | import cc.mrbird.febs.common.domain.FebsResponse; 5 | import cc.mrbird.febs.common.exception.FebsException; 6 | import cc.mrbird.febs.common.utils.HttpUtil; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.shiro.authz.annotation.RequiresPermissions; 9 | import org.springframework.validation.annotation.Validated; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import javax.validation.constraints.NotBlank; 15 | 16 | @Slf4j 17 | @Validated 18 | @RestController 19 | @RequestMapping("weather") 20 | public class WeatherController { 21 | 22 | @GetMapping 23 | @RequiresPermissions("weather:view") 24 | public FebsResponse queryWeather(@NotBlank(message = "{required}") String areaId) throws FebsException { 25 | try { 26 | String data = HttpUtil.sendPost(FebsConstant.MEIZU_WEATHER_URL, "cityIds=" + areaId); 27 | return new FebsResponse().data(data).code("200").message("天气查询成功").status("success"); 28 | } catch (Exception e) { 29 | String message = "天气查询失败"; 30 | log.error(message, e); 31 | throw new FebsException(message); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /backend/src/main/resources/ValidationMessages.properties: -------------------------------------------------------------------------------- 1 | required=\u4E0D\u80FD\u4E3A\u7A7A 2 | range=\u6709\u6548\u957f\u5ea6{min}\u5230{max}\u4e2a\u5b57\u7b26 3 | email=\u90ae\u7bb1\u683c\u5f0f\u4e0d\u5408\u6cd5 4 | mobile=\u624b\u673a\u53f7\u4e0d\u5408\u6cd5 5 | noMoreThan=\u957f\u5ea6\u4e0d\u80fd\u8d85\u8fc7{max}\u4e2a\u5b57\u7b26 6 | invalid=\u503c\u4e0d\u5408\u6cd5 -------------------------------------------------------------------------------- /backend/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | |-----------------------------------------| 2 | | ____ ____ ___ __ | 3 | | | |_ | |_ | |_) ( (` | 4 | | |_| |_|__ |_|_) _)_) | 5 | | | 6 | | Shiro Vue edition v1.0 | 7 | | Spring-Boot-Version: ${spring-boot.version} | 8 | | QQ group : 715689981 | 9 | |-----------------------------------------| -------------------------------------------------------------------------------- /backend/src/main/resources/generator/templates/controller.java.ftl: -------------------------------------------------------------------------------- 1 | package ${package.Controller}; 2 | 3 | 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | <#if restControllerStyle> 7 | import org.springframework.web.bind.annotation.RestController; 8 | <#else> 9 | import org.springframework.stereotype.Controller; 10 | 11 | <#if superControllerClassPackage??> 12 | import ${superControllerClassPackage}; 13 | 14 | 15 | /** 16 | * @author ${author} 17 | */ 18 | <#if restControllerStyle> 19 | @RestController 20 | <#else> 21 | @Controller 22 | 23 | @RequestMapping("<#if package.ModuleName??>/${package.ModuleName}/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}") 24 | <#if kotlin> 25 | class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}() 26 | <#else> 27 | <#if superControllerClass??> 28 | public class ${table.controllerName} extends ${superControllerClass} { 29 | <#else> 30 | public class ${table.controllerName} { 31 | 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /backend/src/main/resources/generator/templates/mapper.java.ftl: -------------------------------------------------------------------------------- 1 | package ${package.Mapper}; 2 | 3 | import ${package.Entity}.${entity}; 4 | import ${superMapperClassPackage}; 5 | 6 | /** 7 | * @author ${author} 8 | */ 9 | <#if kotlin> 10 | interface ${table.mapperName} : ${superMapperClass}<${entity}> 11 | <#else> 12 | public interface ${table.mapperName} extends ${superMapperClass}<${entity}> { 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /backend/src/main/resources/generator/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 | 20 | 21 | <#list table.fields as field> 22 | <#if !field.keyFlag><#--生成普通字段 --> 23 | 24 | 25 | 26 | 27 | 28 | 29 | <#if baseColumnList> 30 | 31 | 32 | <#list table.commonFields as field> 33 | ${field.name}, 34 | 35 | ${table.fieldNames} 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /backend/src/main/resources/generator/templates/service.java.ftl: -------------------------------------------------------------------------------- 1 | package ${package.Service}; 2 | 3 | import ${package.Entity}.${entity}; 4 | import ${superServiceClassPackage}; 5 | 6 | /** 7 | * @author ${author} 8 | */ 9 | <#if kotlin> 10 | interface ${table.serviceName} : ${superServiceClass}<${entity}> 11 | <#else> 12 | public interface ${table.serviceName} extends ${superServiceClass}<${entity}> { 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /backend/src/main/resources/generator/templates/serviceImpl.java.ftl: -------------------------------------------------------------------------------- 1 | package ${package.ServiceImpl}; 2 | 3 | import ${package.Entity}.${entity}; 4 | import ${package.Mapper}.${table.mapperName}; 5 | import ${package.Service}.${table.serviceName}; 6 | import ${superServiceImplClassPackage}; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * @author ${author} 11 | */ 12 | @Service 13 | <#if kotlin> 14 | open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} { 15 | 16 | } 17 | <#else> 18 | public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} { 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /backend/src/main/resources/ip2region/ip2region.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/backend/src/main/resources/ip2region/ip2region.db -------------------------------------------------------------------------------- /backend/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | febs 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ${log.colorPattern} 13 | 14 | 15 | 16 | 17 | 18 | 19 | ${log.path}/info/info.%d{yyyy-MM-dd}.log 20 | ${log.maxHistory} 21 | 22 | 23 | ${log.pattern} 24 | 25 | 26 | INFO 27 | ACCEPT 28 | DENY 29 | 30 | 31 | 32 | 33 | 34 | ${log.path}/error/error.%d{yyyy-MM-dd}.log 35 | 36 | 37 | ${log.pattern} 38 | 39 | 40 | ERROR 41 | ACCEPT 42 | DENY 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /backend/src/main/resources/mapper/job/JobMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /backend/src/main/resources/mapper/system/LoginLogMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 15 | 16 | 35 | -------------------------------------------------------------------------------- /backend/src/main/resources/mapper/system/MenuMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 32 | 33 | 46 | 47 | 55 | -------------------------------------------------------------------------------- /backend/src/main/resources/mapper/system/RoleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | -------------------------------------------------------------------------------- /backend/src/main/resources/mapper/system/UserRoleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | DELETE FROM t_user_role WHERE user_id = #{userId} 14 | 15 | 16 | 17 | 18 | DELETE FROM t_user_role WHERE role_id = #{roleId} 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /backend/src/main/resources/spy.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/backend/src/main/resources/spy.properties -------------------------------------------------------------------------------- /frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=vue 2 | 3 | *.css linguist-language=vue 4 | 5 | *.html linguist-language=vue 6 | -------------------------------------------------------------------------------- /frontend/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | if(!process.env.NODE_ENV){ 5 | process.env.NODE_ENV = 'production' 6 | } 7 | 8 | const ora = require('ora') 9 | const rm = require('rimraf') 10 | const path = require('path') 11 | const chalk = require('chalk') 12 | const webpack = require('webpack') 13 | const config = require('../config') 14 | const webpackConfig = require('./webpack.prod.conf') 15 | 16 | const spinner = ora('building for production...') 17 | spinner.start() 18 | 19 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 20 | if (err) throw err 21 | webpack(webpackConfig, (err, stats) => { 22 | spinner.stop() 23 | if (err) throw err 24 | process.stdout.write(stats.toString({ 25 | colors: true, 26 | modules: false, 27 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 28 | chunks: false, 29 | chunkModules: false 30 | }) + '\n\n') 31 | 32 | if (stats.hasErrors()) { 33 | console.log(chalk.red(' Build failed with errors.\n')) 34 | process.exit(1) 35 | } 36 | 37 | console.log(chalk.cyan(' Build complete.\n')) 38 | console.log(chalk.yellow( 39 | ' Tip: built files are meant to be served over an HTTP server.\n' + 40 | ' Opening index.html over file:// won\'t work.\n' 41 | )) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /frontend/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontend/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/build/logo.png -------------------------------------------------------------------------------- /frontend/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"', 7 | CONFIG_TEXT: '"开发环境"', 8 | BACKEND_API_HOST: '"127.0.0.1:9527"', 9 | HOST: '"127.0.0.1"', 10 | PORT: '"8081"' 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | // Use Eslint Loader? 24 | // If true, your code will be linted during bundling and 25 | // linting errors and warnings will be shown in the console. 26 | useEslint: true, 27 | // If true, eslint errors and warnings will also be shown in the error overlay 28 | // in the browser. 29 | showEslintErrorsInOverlay: false, 30 | 31 | /** 32 | * Source Maps 33 | */ 34 | 35 | // https://webpack.js.org/configuration/devtool/#development 36 | devtool: 'cheap-module-eval-source-map', 37 | 38 | // If you have problems debugging vue-files in devtools, 39 | // set this to false - it *may* help 40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 41 | cacheBusting: true, 42 | 43 | cssSourceMap: true 44 | }, 45 | 46 | build: { 47 | // Template for index.html 48 | index: path.resolve(__dirname, '../dist/index.html'), 49 | 50 | // Paths 51 | assetsRoot: path.resolve(__dirname, '../dist'), 52 | assetsSubDirectory: 'static', 53 | assetsPublicPath: './', 54 | 55 | /** 56 | * Source Maps 57 | */ 58 | 59 | productionSourceMap: true, 60 | // https://webpack.js.org/configuration/devtool/#production 61 | devtool: '#source-map', 62 | 63 | // Gzip off by default as many popular static hosts such as 64 | // Surge or Netlify already gzip all static assets for you. 65 | // Before setting to `true`, make sure to: 66 | // npm install --save-dev compression-webpack-plugin 67 | productionGzip: true, 68 | productionGzipExtensions: ['js', 'css'], 69 | 70 | // Run the build command with an extra argument to 71 | // View the bundle analyzer report after build finishes: 72 | // `npm run build --report` 73 | // Set to `true` or `false` to always turn it on or off 74 | bundleAnalyzerReport: true 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /frontend/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | CONFIG_TEXT: '"生产环境"', 5 | BACKEND_API_HOST: '"127.0.0.1:9527"', 6 | bundleAnalyzerReport: false 7 | } 8 | -------------------------------------------------------------------------------- /frontend/config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"', 7 | CONFIG_TEXT: '"测试环境"', 8 | BACKEND_API_HOST: '"127.0.0.1:9527"', 9 | bundleAnalyzerReport: false 10 | }) 11 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FEBS 权限系统 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/datetime/RangeDate.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 29 | -------------------------------------------------------------------------------- /frontend/src/components/exception/ExceptionPage.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 35 | 36 | 72 | -------------------------------------------------------------------------------- /frontend/src/components/exception/typeConfig.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | 403: { 3 | img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg', 4 | title: '403', 5 | desc: '抱歉,你无权访问该页面' 6 | }, 7 | 404: { 8 | img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg', 9 | title: '404', 10 | desc: '抱歉,你访问的页面不存在或仍在开发中' 11 | }, 12 | 500: { 13 | img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg', 14 | title: '500', 15 | desc: '抱歉,服务器出错了' 16 | } 17 | } 18 | 19 | export default config 20 | -------------------------------------------------------------------------------- /frontend/src/components/menu/Contextmenu.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 62 | 63 | 72 | -------------------------------------------------------------------------------- /frontend/src/components/setting/SettingItem.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 15 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/setting/StyleItem.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 37 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Febs from './FEBS' 3 | import router from './router' 4 | import Antd from 'ant-design-vue' 5 | import store from './store' 6 | import request from 'utils/request' 7 | import db from 'utils/localstorage' 8 | import AesEncrypt from 'utils/aesEncrypt' 9 | import VueMeta from 'vue-meta' 10 | import 'ant-design-vue/dist/antd.css' 11 | import echarts from 'echarts' 12 | import 'utils/install' 13 | 14 | Vue.config.productionTip = false 15 | Vue.use(Antd) 16 | Vue.use(db) 17 | Vue.use(VueMeta) 18 | Vue.prototype.$echarts = echarts 19 | Vue.use({ 20 | install (Vue) { 21 | Vue.prototype.$db = db 22 | } 23 | }) 24 | 25 | Vue.prototype.$post = request.post 26 | Vue.prototype.$get = request.get 27 | Vue.prototype.$put = request.put 28 | Vue.prototype.$delete = request.delete 29 | Vue.prototype.$export = request.export 30 | Vue.prototype.$download = request.download 31 | Vue.prototype.$upload = request.upload 32 | Vue.prototype.$originalGet = request.originalGet 33 | Vue.prototype.$aesEncrypt = AesEncrypt 34 | 35 | /* eslint-disable no-new */ 36 | new Vue({ 37 | router, 38 | store, 39 | render: h => h(Febs) 40 | }).$mount('#febs') 41 | -------------------------------------------------------------------------------- /frontend/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import account from './modules/account' 4 | import setting from './modules/setting' 5 | import dict from './modules/dict' 6 | 7 | Vue.use(Vuex) 8 | 9 | export default new Vuex.Store({ 10 | modules: { 11 | account, 12 | setting, 13 | dict 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /frontend/src/store/modules/account.js: -------------------------------------------------------------------------------- 1 | import db from 'utils/localstorage' 2 | 3 | export default { 4 | namespaced: true, 5 | state: { 6 | token: db.get('USER_TOKEN'), 7 | expireTime: db.get('EXPIRE_TIME'), 8 | user: db.get('USER'), 9 | permissions: db.get('PERMISSIONS'), 10 | roles: db.get('ROLES') 11 | }, 12 | mutations: { 13 | setToken (state, val) { 14 | db.save('USER_TOKEN', val) 15 | state.token = val 16 | }, 17 | setExpireTime (state, val) { 18 | db.save('EXPIRE_TIME', val) 19 | state.expireTime = val 20 | }, 21 | setUser (state, val) { 22 | db.save('USER', val) 23 | state.user = val 24 | }, 25 | setPermissions (state, val) { 26 | db.save('PERMISSIONS', val) 27 | state.permissions = val 28 | }, 29 | setRoles (state, val) { 30 | db.save('ROLES', val) 31 | state.roles = val 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/store/modules/dict.js: -------------------------------------------------------------------------------- 1 | import db from 'utils/localstorage' 2 | 3 | export default { 4 | namespaced: true, 5 | state: { 6 | dicts: db.get('DICTS') 7 | }, 8 | mutations: { 9 | setDicts (state, val) { 10 | db.save('DICTS', val) 11 | state.dicts = val 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/store/modules/setting.js: -------------------------------------------------------------------------------- 1 | import db from 'utils/localstorage' 2 | 3 | export default { 4 | namespaced: true, 5 | state: { 6 | sidebar: { 7 | opened: true 8 | }, 9 | settingBar: { 10 | opened: false 11 | }, 12 | isMobile: false, 13 | theme: db.get('THEME', 'light'), 14 | layout: db.get('LAYOUT', 'side'), 15 | systemName: 'FEBS 权限系统', 16 | copyright: `${new Date().getFullYear()} MrBird`, 17 | multipage: getBooleanValue(db.get('MULTIPAGE'), true), 18 | fixSiderbar: getBooleanValue(db.get('FIX_SIDERBAR'), true), 19 | fixHeader: getBooleanValue(db.get('FIX_HEADER'), true), 20 | colorList: [ 21 | 'rgb(245, 34, 45)', 22 | 'rgb(250, 84, 28)', 23 | 'rgb(250, 173, 20)', 24 | 'rgb(66, 185, 131)', 25 | 'rgb(82, 196, 26)', 26 | 'rgb(24, 144, 255)', 27 | 'rgb(47, 84, 235)', 28 | 'rgb(114, 46, 209)' 29 | ], 30 | color: db.get('COLOR', 'rgb(24, 144, 255)') 31 | }, 32 | mutations: { 33 | setDevice (state, isMobile) { 34 | state.isMobile = isMobile 35 | }, 36 | setTheme (state, theme) { 37 | db.save('THEME', theme) 38 | state.theme = theme 39 | }, 40 | setLayout (state, layout) { 41 | db.save('LAYOUT', layout) 42 | state.layout = layout 43 | }, 44 | setMultipage (state, multipage) { 45 | db.save('MULTIPAGE', multipage) 46 | state.multipage = multipage 47 | }, 48 | setSidebar (state, type) { 49 | state.sidebar.opened = type 50 | }, 51 | fixSiderbar (state, flag) { 52 | db.save('FIX_SIDERBAR', flag) 53 | state.fixSiderbar = flag 54 | }, 55 | fixHeader (state, flag) { 56 | db.save('FIX_HEADER', flag) 57 | state.fixHeader = flag 58 | }, 59 | setSettingBar (state, flag) { 60 | state.settingBar.opened = flag 61 | }, 62 | setColor (state, color) { 63 | db.save('COLOR', color) 64 | state.color = color 65 | } 66 | } 67 | } 68 | 69 | function getBooleanValue (value, defaultValue) { 70 | if (Object.is(value, null)) { 71 | return defaultValue 72 | } 73 | if (JSON.stringify(value) !== '{}') { 74 | return value 75 | } else { 76 | return false 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /frontend/src/utils/aesEncrypt.js: -------------------------------------------------------------------------------- 1 | import CryptoJS from 'crypto-js' 2 | 3 | export default { 4 | encrypt: function (data) { 5 | /* AES前端加密 */ 6 | var key = CryptoJS.enc.Utf8.parse('4Dd2Bb3Cc1Aa5Ee0') 7 | var iv = CryptoJS.enc.Utf8.parse('4Dd2Bb3Cc1Aa5Ee0') 8 | var str = CryptoJS.AES.encrypt(data, key, {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding}).toString() 9 | return this.filter(str) 10 | }, 11 | filter: function (value) { 12 | value = value.replace('#', '%23') 13 | .replace('%', '%25') 14 | .replace('&', '%26') 15 | .replace('+', '%2B') 16 | .replace('//', '%2F') 17 | .replace('?', '%3F') 18 | return value 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/utils/color.js: -------------------------------------------------------------------------------- 1 | import { message } from 'ant-design-vue/es' 2 | 3 | let lessNodesAppended 4 | const updateTheme = primaryColor => { 5 | if (!primaryColor) { 6 | return 7 | } 8 | const hideMessage = message.loading('加载主题...', 0) 9 | function buildIt () { 10 | if (!window.less) { 11 | return 12 | } 13 | setTimeout(() => { 14 | window.less 15 | .modifyVars({ 16 | '@primary-color': primaryColor 17 | }) 18 | .then(() => { 19 | hideMessage() 20 | }) 21 | .catch((e) => { 22 | console.log(e) 23 | message.error('Failed to update theme') 24 | hideMessage() 25 | }) 26 | }, 200) 27 | } 28 | if (!lessNodesAppended) { 29 | // insert less.js and color.less 30 | const lessStyleNode = document.createElement('link') 31 | const lessConfigNode = document.createElement('script') 32 | const lessScriptNode = document.createElement('script') 33 | let ctx = window.document.location.pathname === '/' ? '' : window.document.location.pathname 34 | lessStyleNode.setAttribute('rel', 'stylesheet/less') 35 | lessStyleNode.setAttribute('href', ctx + '/static/less/Color.less') 36 | lessConfigNode.innerHTML = ` 37 | window.less = { 38 | async: true, 39 | env: 'production', 40 | javascriptEnabled: true 41 | } 42 | ` 43 | // https://cdn.bootcss.com/less.js/3.9.0/less.min.js 44 | lessScriptNode.src = ctx + '/static/less/less.min.js' 45 | lessScriptNode.async = true 46 | lessScriptNode.onload = () => { 47 | buildIt() 48 | lessScriptNode.onload = null 49 | } 50 | document.body.appendChild(lessStyleNode) 51 | document.body.appendChild(lessConfigNode) 52 | document.body.appendChild(lessScriptNode) 53 | lessNodesAppended = true 54 | } else { 55 | buildIt() 56 | } 57 | } 58 | export { updateTheme } 59 | -------------------------------------------------------------------------------- /frontend/src/utils/common.js: -------------------------------------------------------------------------------- 1 | export function triggerWindowResizeEvent () { 2 | let event = document.createEvent('HTMLEvents') 3 | event.initEvent('resize', true, true) 4 | event.eventType = 'message' 5 | window.dispatchEvent(event) 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/utils/device.js: -------------------------------------------------------------------------------- 1 | import enquireJs from 'enquire.js' 2 | 3 | const enquireScreen = function (call) { 4 | const hanlder = { 5 | match: function () { 6 | call && call(true) 7 | }, 8 | unmatch: function () { 9 | call && call(false) 10 | } 11 | } 12 | enquireJs.register('only screen and (max-width: 767.99px)', hanlder) 13 | } 14 | 15 | export default enquireScreen 16 | -------------------------------------------------------------------------------- /frontend/src/utils/install.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import {hasPermission, hasNoPermission, hasAnyPermission, hasRole, hasAnyRole} from 'utils/permissionDirect' 4 | 5 | const Plugins = [ 6 | hasPermission, 7 | hasNoPermission, 8 | hasAnyPermission, 9 | hasRole, 10 | hasAnyRole 11 | ] 12 | 13 | Plugins.map((plugin) => { 14 | Vue.use(plugin) 15 | }) 16 | 17 | export default Vue 18 | -------------------------------------------------------------------------------- /frontend/src/utils/localstorage.js: -------------------------------------------------------------------------------- 1 | let db = { 2 | save (key, value) { 3 | localStorage.setItem(key, JSON.stringify(value)) 4 | }, 5 | get (key, defaultValue = {}) { 6 | return JSON.parse(localStorage.getItem(key)) || defaultValue 7 | }, 8 | remove (key) { 9 | localStorage.removeItem(key) 10 | }, 11 | clear () { 12 | localStorage.clear() 13 | } 14 | } 15 | 16 | export default db 17 | -------------------------------------------------------------------------------- /frontend/src/utils/utils.less: -------------------------------------------------------------------------------- 1 | .textOverflow() { 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | word-break: break-all; 5 | white-space: nowrap; 6 | } 7 | 8 | .textOverflowMulti(@line: 3, @bg: #fff) { 9 | overflow: hidden; 10 | position: relative; 11 | line-height: 1.5em; 12 | max-height: @line * 1.5em; 13 | text-align: justify; 14 | margin-right: -1em; 15 | padding-right: 1em; 16 | &:before { 17 | background: @bg; 18 | content: '...'; 19 | padding: 0 1px; 20 | position: absolute; 21 | right: 14px; 22 | bottom: 0; 23 | } 24 | &:after { 25 | background: white; 26 | content: ''; 27 | margin-top: 0.2em; 28 | position: absolute; 29 | right: 14px; 30 | width: 1em; 31 | height: 1em; 32 | } 33 | } 34 | 35 | .clearfix() { 36 | zoom: 1; 37 | &:before, 38 | &:after { 39 | content: ' '; 40 | display: table; 41 | } 42 | &:after { 43 | clear: both; 44 | visibility: hidden; 45 | font-size: 0; 46 | height: 0; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /frontend/src/views/common/EmptyPageView.vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | -------------------------------------------------------------------------------- /frontend/src/views/common/GlobalFooter.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 37 | -------------------------------------------------------------------------------- /frontend/src/views/common/HeadInfo.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 27 | 28 | 57 | -------------------------------------------------------------------------------- /frontend/src/views/common/PageContent.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 43 | 44 | 109 | -------------------------------------------------------------------------------- /frontend/src/views/common/PageLayout.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 38 | 39 | 62 | -------------------------------------------------------------------------------- /frontend/src/views/common/PageView.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 38 | 39 | 49 | -------------------------------------------------------------------------------- /frontend/src/views/common/RunningTask.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 44 | 45 | 48 | -------------------------------------------------------------------------------- /frontend/src/views/error/403.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /frontend/src/views/error/404.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /frontend/src/views/error/500.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /frontend/src/views/monitor/RedisTerminal.vue: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | -------------------------------------------------------------------------------- /frontend/src/views/system/dept/DeptInputTree.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 36 | -------------------------------------------------------------------------------- /frontend/src/views/system/menu/Icon.less: -------------------------------------------------------------------------------- 1 | @active-color: #4a4a48; 2 | ul { 3 | max-height: 700px; 4 | overflow-y: auto; 5 | padding-left: .5rem; 6 | i { 7 | font-size: 1.5rem; 8 | border: 1px solid #f1f1f1; 9 | padding: .2rem; 10 | margin: .3rem; 11 | cursor: pointer; 12 | &.active, &:hover { 13 | border-radius: 2px; 14 | border-color: @active-color; 15 | background-color: @active-color; 16 | color: #fff; 17 | transition: all .3s; 18 | } 19 | } 20 | li { 21 | list-style: none; 22 | float: left; 23 | width: 5%; 24 | text-align: center; 25 | cursor: pointer; 26 | color: #555; 27 | transition: color .3s ease-in-out,background-color .3s ease-in-out; 28 | position: relative; 29 | margin: 3px 0; 30 | border-radius: 4px; 31 | background-color: #fff; 32 | overflow: hidden; 33 | padding: 10px 0 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/views/system/role/RoleInfo.vue: -------------------------------------------------------------------------------- 1 | 28 | 86 | -------------------------------------------------------------------------------- /frontend/src/views/system/user/UserInfo.less: -------------------------------------------------------------------------------- 1 | .user-info { 2 | background: #fff; 3 | padding: 0 10px 10px 10px; 4 | } 5 | .user-info-side { 6 | background: #fff; 7 | } 8 | .user-info-side { 9 | max-width: 10rem !important; 10 | min-width: 10rem !important; 11 | width: 10rem !important; 12 | } 13 | .user-content-one{ 14 | margin-right: 1.2rem; 15 | } 16 | p { 17 | margin-bottom: 1rem; 18 | max-width: 15.5rem; 19 | } 20 | i { 21 | margin-right: .8rem; 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/views/web/DailyArticle.vue: -------------------------------------------------------------------------------- 1 | 13 | 59 | 85 | -------------------------------------------------------------------------------- /frontend/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/.gitkeep -------------------------------------------------------------------------------- /frontend/static/avatar/17e420c250804efe904a09a33796d5a10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/17e420c250804efe904a09a33796d5a10.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/17e420c250804efe904a09a33796d5a16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/17e420c250804efe904a09a33796d5a16.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/19034103295190235.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/19034103295190235.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/1d22f3e41d284f50b2c8fc32e0788698.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/1d22f3e41d284f50b2c8fc32e0788698.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165754.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165754.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165815.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165815.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165821.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165821.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165827.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165827.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165834.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165834.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165840.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165840.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165846.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165846.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165855.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165855.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165909.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165909.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165914.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165914.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165920.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165920.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165927.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165927.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165936.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165936.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165942.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165942.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165947.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165947.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414165955.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414165955.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/20180414170003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/20180414170003.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/2dd7a2d09fa94bf8b5c52e5318868b4d9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/2dd7a2d09fa94bf8b5c52e5318868b4d9.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/2dd7a2d09fa94bf8b5c52e5318868b4df.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/2dd7a2d09fa94bf8b5c52e5318868b4df.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/496b3ace787342f7954b7045b8b06804.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/496b3ace787342f7954b7045b8b06804.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/595ba7b05f2e485eb50565a50cb6cc3c.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/595ba7b05f2e485eb50565a50cb6cc3c.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/5997fedcc7bd4cffbd350b40d1b5b9824.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/5997fedcc7bd4cffbd350b40d1b5b9824.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/5997fedcc7bd4cffbd350b40d1b5b987.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/5997fedcc7bd4cffbd350b40d1b5b987.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/87d8194bc9834e9f8f0228e9e530beb1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/87d8194bc9834e9f8f0228e9e530beb1.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/8f5b60ef00714a399ee544d331231820.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/8f5b60ef00714a399ee544d331231820.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/964e40b005724165b8cf772355796c8c.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/964e40b005724165b8cf772355796c8c.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/BiazfanxmamNRoxxVxka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/BiazfanxmamNRoxxVxka.png -------------------------------------------------------------------------------- /frontend/static/avatar/WhxKECPNujWoWEFNdnJE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/WhxKECPNujWoWEFNdnJE.png -------------------------------------------------------------------------------- /frontend/static/avatar/a3b10296862e40edb811418d64455d00.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/a3b10296862e40edb811418d64455d00.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/a43456282d684e0b9319cf332f8ac468.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/a43456282d684e0b9319cf332f8ac468.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/bba284ac05b041a8b8b0d1927868d5c9x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/bba284ac05b041a8b8b0d1927868d5c9x.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/c7c4ee7be3eb4e73a19887dc713505145.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/c7c4ee7be3eb4e73a19887dc713505145.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/cnrhVkzwxjPwAaCfPbdc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/cnrhVkzwxjPwAaCfPbdc.png -------------------------------------------------------------------------------- /frontend/static/avatar/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/default.jpg -------------------------------------------------------------------------------- /frontend/static/avatar/ff698bb2d25c4d218b3256b46c706ece.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/ff698bb2d25c4d218b3256b46c706ece.jpeg -------------------------------------------------------------------------------- /frontend/static/avatar/gaOngJwsRYRaVAuXXcmB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/gaOngJwsRYRaVAuXXcmB.png -------------------------------------------------------------------------------- /frontend/static/avatar/jZUIxmJycoymBprLOUbT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/jZUIxmJycoymBprLOUbT.png -------------------------------------------------------------------------------- /frontend/static/avatar/ubnKSIfAJTxIgXOKlciN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/avatar/ubnKSIfAJTxIgXOKlciN.png -------------------------------------------------------------------------------- /frontend/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/img/favicon.ico -------------------------------------------------------------------------------- /frontend/static/img/logo-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/img/logo-blue.png -------------------------------------------------------------------------------- /frontend/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/frontend/static/img/logo.png -------------------------------------------------------------------------------- /frontend/static/img/side-bar-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 Copy 5 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /frontend/static/img/side-bar-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 Copy 5 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/static/img/side-bar-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /frontend/static/img/side-bar-top.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /frontend/static/less/Common.less: -------------------------------------------------------------------------------- 1 | .search{ 2 | margin-bottom: 54px; 3 | } 4 | .fold{ 5 | width: calc(100% - 216px); 6 | display: inline-block 7 | } 8 | .operator{ 9 | margin-bottom: 18px; 10 | } 11 | @media screen and (max-width: 900px) { 12 | .fold { 13 | width: 100%; 14 | } 15 | } 16 | .operator button { 17 | margin-right: 5px; 18 | } 19 | i { 20 | cursor: pointer; 21 | } 22 | -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/1.png -------------------------------------------------------------------------------- /images/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/10.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/2.png -------------------------------------------------------------------------------- /images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/3.png -------------------------------------------------------------------------------- /images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/4.png -------------------------------------------------------------------------------- /images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/6.png -------------------------------------------------------------------------------- /images/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/7.png -------------------------------------------------------------------------------- /images/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/8.png -------------------------------------------------------------------------------- /images/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/9.png -------------------------------------------------------------------------------- /images/QQ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/QQ.jpg -------------------------------------------------------------------------------- /images/request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx7614140/FEBS-Vue-Fix/ff24a3266372d1a0e750541a5c7755c7975837c8/images/request.png --------------------------------------------------------------------------------