开源软件名称:cmdAdmin
开源软件地址:https://gitee.com/cmdshare/cmdAdmin
开源软件介绍:
cmdAdminQQ群:951379340 介绍基于SpringBoot+SpringMVC+MybatisPlus分布式敏捷开发系统架构,默认支持SAAS化,努力为中小型企业打造全方位J2EE企业级开发解决方案。 软件架构cmdAdmin是基于Spring Cloud Alibaba体系微服务化分布式敏捷开发系统架构,支持SAAS化,提供整套公共微服务服务模块,其中包含用户管理、资源权限管理等多个模块,作为后端服务的开发脚手架。代码简洁,架构清晰,适合学习和直接项目中使用。 核心技术采用Spring Boot 2.2.7以及Spring Cloud 相关核心组件,采用Nacos注册和配置中心,集成流量卫兵Sentinel,dubbo远程调用,集成Activiti工作流,动态表单+动态工作流,工作流下拉选择代理人,前端采用vue-element-admin组件。 安装教程- 安装redis jdk maven
- 安装Nacos 导入doc下文件 导入数据库sql
- 启动网关 admin log workflow 模块
使用说明有问题请加QQ群: 951379340 - 超级管理员账号 13455555555/admin123 不参与业务只管理租户和菜单维护(操作哪些菜单租户可见)
- 租户管理1 13466666666/123456
- 租户管理2 13466666666/123456
主要功能用户中心 - 用户管理:提供用户的相关配置,新增用户后,默认密码为 cmd.c0m
- 在线用户:统计登录用户
- 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限
- 部门管理:可配置系统组织架构,树形表格展示
系统中心 - 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单
- 字典管理:可维护常用一些固定的数据,如:性别等
- 存储管理: 上传附件记录
- 表单配置:根据 form-generator 集成的表单拖拽
- 数据管理: 动态表单的集中数据管理列表
日志中心 - 日志管理:记录用户操作日志与异常日志,方便开发人员定位排错
- ELK 日志:未完成
流程中心 - 流程管理:手绘工作流模型
- 待办任务:当前人审批任务
- 请假申请:动态配置,阅读表单配置
- 财务申请: 动态配置,阅读表单配置
项目结构cmdAdmin-common 为系统的公共模块,各种工具类,公共配置存在该模块 cmdAdmin-gateway 网关层,所有入库经过该服务 cmdAdmin-modules 为系统的应用模块 cmdAdmin-modules-admin 基础模块 cmdAdmin-modules-log 日志模块 cmdAdmin-modules-workflow 工作流模块 cmdAdmin-rpc 远程调用 cmdadmin-template 前端页面
运行项目安装 Redis 安装 Nacos 创建 cmdadmin 数据库,导入 db 下的 sql 脚本 打开 nacos,点开配置管理--配置列表 --导入配置 doc 下的 xxx.zip,会出现配置列表 修改 cmdAdmin-modules-admin-dev.yml master 中的数据库配置,slave 是 enabled 是 false,如果需要开启也改一下数据库链接,其他模块类似,其他配置根据需求改动, registry.address 是 nacos 地址 - 启动 cmdAdmin-gateway cmdAdmin-modules-admin 这 2 个启动就可以登录看基础功能了 日志和工作流根据需要启动
- 启动 cmdadmin-template 前端 npm install , npm run dev
环境配置具体安装问题自行百度 运行环境准备- JDK1.8+
- Redis
- MYSQL5.6+
- Maven3+
- Nacos1.3+
开发环境- IDEA、Eclipse、vscode
- 开发插件
- Lombok:节省时间必备
- 阿里 JAVA 开发规约插件:P3C
- JRebel:秒级热更新神器必备
导入 SQl 文件- MySQL 数据库新建数据库,导入 doc 下的 sql 文件
配置文件导入- 启动 nacos 后 导入 doc 下 zip 文件
- 编辑对应配置文件 比如,数据库配置
访问测试开发手册当前用户获取当前登录用户对象 OnlineUser user = securityAdmin.currentUser(token);例如:@GetMapping(value = "/menus")public ResponseEntity menus(HttpServletRequest request, @RequestHeader(value = Constants.TOKEN) String token) { CmdResponse response = new CmdResponse(); response.setData(loginService.listMenus(token)); return ResponseEntity.ok(response);}@Override public List<MenuVO> listMenus(String token) { OnlineUser user = securityAdmin.currentUser(token); Map<String, Object> param = Maps.newHashMap(); List<MenuVO> vos = baseMapper.getMenus(param); return vos; } 权限控制本系统权限控制采用 RBAC 思想。简单地说,一个用户拥有若干角色,每一个角色拥有若干个菜单,菜单中存在菜单权限与按钮权限, 这样,就构造成“用户-角色-菜单” 的授权模型。在这种模型中,用户与角色、角色与菜单之间构成了多对多的关系,如下图 权限控制本系统安全框架使用的是 Jwt Token, 访问后端接口需在请求头中携带 token 进行访问,请求头格式如下: Authorization: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ7XCJjcmVhdGVUaW1lXCI6MTU5ODE3OTc4NDAwMCxcImRlbGV0ZWRcIjp0cnVlLFwiZGVwdElkXCI6XCIwZjY4NjZiZTcwYTJmNjkwZTY3YWQyYjZiNzExMjZiYlwiLFwiZGVwdE5hbWVcIjpcIumZleilv1wiLFwiZW5hYmxlZFwiOnRydWUsXCJpZFwiOlwid3d3XCIsXCJuaWNrTmFtZVwiOlwiY21kYWRtaW5cIixcInBhc3N3b3JkXCI6XCIyMmJiZmM0NmZlNzEwOTlkMTM5ZTJjYWEwOGQ1NWJlN1wiLFwicGhvbmVcIjpcIjEzNDU1NTU1NTU1XCIsXCJzYWx0XCI6XCIxMTFcIixcInVwZGF0ZVRpbWVcIjoxNTk4MTc5Nzg2MDAwLFwidXNlck5hbWVcIjpcImNtZGFkbWluXCJ9IiwianRpIjoiN2UzNzU3ZjUtZmU0MC00NWRhLTk2OTctN2ZmNTk3NGE1Y2RjIn0.zM1UafO-UDVrnmsBTTtQPAThVOddR5cz0gDKvjCAlrSfqj5rw-lU4MQmYFvM4k5NNy6NbRQEw5YER3k19C1ARg 权限注解提供 EL 表达式,允许在定义接口访问的方法上面添加注解,来控制访问权限,常用的 EL 如下 表达式 | 描述 |
---|
@CmdAdmin({"user:list"}) | 当前用户是否拥有指定角色。 | @CmdAdmin({"admin","user:list"}) | 多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回 true。 |
下面的接口表示用户拥有 admin、user:add、user:update create 如果方法不加@CmdAdmin 注解,意味着所有用户都需要带上有效的 token 后能访问 create 方法 @Log("用户新增或编辑")@CmdAdmin({"admin","user:add","user:update"})@PutMappingpublic ResponseEntity<CmdResponse> create(@RequestBody User entity) throws Exception { userService.create(entity); return ResponseEntity.ok(new CmdResponse());} 由于每个接口都需要给超级管理员放行,而使用 CmdAdmin('admin','user:list') 每次都需要重复的添加 admin 权限 匿名访问在我们使用的时候,有些接口是不需要验证权限的,这个时候就需要我们给接口放行,在网关配置中添加,使用方式如下 cmd: filter: #需要进行过滤的白名单 allowPaths: - /admin/cmd/v1/auth/login - /admin/cmd/v1/auth/code 远程调用本系统使用的是Dubbo远程调用,当接口对外需要提供访问时候,在当前接口实现上加入dubbo注解 例如: @Service@org.apache.dubbo.config.annotation.Service(version = "1.0.0")public class FwLogServiceImpl 如果不对外提供访问,只是内部容器调用则只使用spring的 @Service 即可 自动填充原理: /** * 创建时间 */ // 注意!这里需要标记为填充字段@TableField(value = "create_time",fill = FieldFill.INSERT)private Date createTime; - 自定义实现类 MyMetaObjectHandler
/** * 自动注入 * @author cmd * */@Componentpublic class BasicMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.fillStrategy(metaObject, "createTime", new Date()); this.fillStrategy(metaObject, "enabled", true); this.fillStrategy(metaObject, "deleted", true); } @Override public void updateFill(MetaObject metaObject) { this.fillStrategy(metaObject, "updateTime", new Date()); }} public enum FieldFill { /** * 默认不处理 */ DEFAULT, /** * 插入填充字段 */ INSERT, /** * 更新填充字段 */ UPDATE, /** * 插入和更新填充字段 */ INSERT_UPDATE} 字典翻译在后台字典管理中新增字典数据 参考前端代码views\system\menu\index.vue 使用步骤: - import { getDic } from '@/utils/auth'- created() { this.dicType = getDic('字典编码') }- formatterType({ cellValue }) { const item = this.dicType.find(item => item.code === cellValue) return item ? item.value : '' }- <vxe-table-column field="type" title="菜单类型" :formatter="formatterType" /> 异常处理我们开发项目的时候,数据在请求过程中发生错误是非常常见的事情。 如:权限不足、数据唯一异常、数据不能为空异常、义务异常等。 这些异常如果不经过处理会对前端开发人员和使用者造成不便,因此我们就需要统一处理他们。 代码位置: cmdAdmin-common 工程中com.ninong.ker.common.exception包中处理异常在每个模块 conf 包 LogAspect 异常封装异常实体 /** * 错误类 * * @author cmd * @date 2020年11月28日 下午11:16:13 */ @Data public class CmdError { private Integer status = 400; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime timestamp; private String message; private CmdError() { timestamp = LocalDateTime.now(); } public static CmdError error(String message) { CmdError apiError = new CmdError(); apiError.setMessage(message); return apiError; } public static CmdError error(Integer status, String message) { CmdError apiError = new CmdError(); apiError.setStatus(status); apiError.setMessage(message); return apiError; } } 自定义异常通用异常封装了 CmdException,用于处理通用的异常 /** * @author cmd * @date 2020-11-23 统一异常处理 */ @Getter public class CmdException extends RuntimeException{ /** * */ private static final long serialVersionUID = 1L; private Integer status = BAD_REQUEST.value(); public CmdException(String msg){ super(msg); } public CmdException(Integer status,String msg){ super(msg); this.status = status; } public CmdException(BusinessEnum bus){ super(bus.getMsg()); this.status = bus.getStatus(); } } 全局异常拦截使用全局异常处理器 @RestControllerAdvice 处理请求发送的异常 /\*\* - 异常处理类 - - @author cmd - @date 2020 年 11 月 28 日 下午 11:22:48 \*/ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * 处理所有不可知的异常 */ @ExceptionHandler(Throwable.class) public ResponseEntity<CmdError> handleException(Throwable e) { // 打印堆栈信息 log.error(ThrowableUtil.getStackTrace(e)); return buildResponseEntity(CmdError.error(e.getMessage())); } /** * 处理自定义异常 */ @ExceptionHandler(value = CmdException.class) public ResponseEntity<CmdResponse> badRequestException(CmdException e) { CmdResponse response = new CmdResponse(); response.setCode(e.getStatus()); response.setMessage(e.getMessage()); return ResponseEntity.ok(response); } /** * 接口参数校验不通过异常 * * @param e * @return */ @ExceptionHandler(value = MethodArgumentNotValidException.class) public ResponseEntity<CmdResponse> methodArgumentNotValidException(MethodArgumentNotValidException e) { List<FieldError> bindingResult = e.getBindingResult().getFieldErrors(); StringBuilder sb = new StringBuilder(); for (FieldError fieldError : bindingResult) { sb.append(fieldError.getDefaultMessage()).append(";"); } CmdResponse response = new CmdResponse(); response.setCode(100); response.setMessage(sb.toString()); return ResponseEntity.ok(response); } /** * 统一返回 */ private ResponseEntity<CmdError> buildResponseEntity(CmdError CmdError) { return new ResponseEntity<>(CmdError, HttpStatus.valueOf(CmdError.getStatus())); } } 具体使用 // 通用异常 throw new CmdException("系统提醒"); // 通用异常,使用自定义状态码 throw new CmdException(402, "系统提醒"); 系统日志本系统使用 AOP 方式记录用户操作日志,只需要在 controller 的方法上使用 @Log("") 注解,就可以将用户操作记录到数据库模块具体使用如下: @Log("用户删除")@CmdAdmin({"admin","user:delete"})@DeleteMappingpublic ResponseEntity<CmdResponse> delete(@RequestBody String[] ids) { userService.delete(ids); CmdResponse response = new CmdResponse(); response.setMessage(BusinessEnum.DELETE_SUCCESS.getMsg()); return ResponseEntity.ok(response);} 页面日志中心可以看到 操作日志和异常日志 |
请发表评论