# cmdAdmin
**Repository Path**: wusyJava/cmdAdmin
## Basic Information
- **Project Name**: cmdAdmin
- **Description**: cmdAdmin是基于Spring Cloud Alibaba体系微服务化分布式敏捷开发系统架构,提供整套公共微服务服务模块,其中包含用户管理、资源权限管理等多个模块,作为后端服务的开发脚手架。代码简洁,架构清晰,适合学习和直接项目中使用。 核心技术采用Spring Boot 2.2.7以及Spring Cloud 相关核心组件,采用Nacos注册和配置中心,集成流量卫兵Sentinel,dubbo远程调用,集成Activiti工作流,动态表单+动态工作流,工作流下拉选择代理人,前端采用vue-element-admin组件。
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 20
- **Created**: 2021-11-17
- **Last Updated**: 2021-11-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# cmdAdmin
QQ群: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组件。
# 安装教程
1. 安装redis jdk maven
2. 安装Nacos 导入doc下文件 导入数据库sql
3. 启动网关 admin log workflow 模块
# 使用说明
有问题请加QQ群: 951379340
1. 超级管理员账号 13455555555/admin123 不参与业务只管理租户和菜单维护(操作哪些菜单租户可见)
2. 租户管理1 13466666666/123456
3. 租户管理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 文件
- 编辑对应配置文件 比如,数据库配置
### 访问测试
- 账号 cmdadmin/admin123
# 开发手册
## 当前用户
获取当前登录用户对象
```java
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 listMenus(String token) {
OnlineUser user = securityAdmin.currentUser(token);
Map param = Maps.newHashMap();
List vos = baseMapper.getMenus(param);
return vos;
}
```
## 权限控制
本系统权限控制采用 RBAC 思想。简单地说,一个用户拥有若干角色,每一个角色拥有若干个菜单,菜单中存在菜单权限与按钮权限, 这样,就构造成“用户-角色-菜单” 的授权模型。在这种模型中,用户与角色、角色与菜单之间构成了多对多的关系,如下图

### 权限控制
本系统安全框架使用的是 Jwt Token, 访问后端接口需在请求头中携带 token 进行访问,请求头格式如下:
```yml
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 方法
```java
@Log("用户新增或编辑")
@CmdAdmin({"admin","user:add","user:update"})
@PutMapping
public ResponseEntity create(@RequestBody User entity) throws Exception {
userService.create(entity);
return ResponseEntity.ok(new CmdResponse());
}
```
由于每个接口都需要给超级管理员放行,而使用 CmdAdmin('admin','user:list') 每次都需要重复的添加 admin 权限
### 匿名访问
在我们使用的时候,有些接口是不需要验证权限的,这个时候就需要我们给接口放行,在网关配置中添加,使用方式如下
```yml
cmd:
filter: #需要进行过滤的白名单
allowPaths:
- /admin/cmd/v1/auth/login
- /admin/cmd/v1/auth/code
```
## 远程调用
本系统使用的是Dubbo远程调用,当接口对外需要提供访问时候,在当前接口实现上加入dubbo注解
例如:
```java
@Service
@org.apache.dubbo.config.annotation.Service(version = "1.0.0")
public class FwLogServiceImpl
````
如果不对外提供访问,只是内部容器调用则只使用spring的 @Service 即可
## 自动填充
原理:
- 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
- 注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!
```java
/**
* 创建时间
*/
// 注意!这里需要标记为填充字段
@TableField(value = "create_time",fill = FieldFill.INSERT)
private Date createTime;
```
- 自定义实现类 MyMetaObjectHandler
```java
/**
* 自动注入
* @author cmd
*
*/
@Component
public 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());
}
}
```
- FieldFill类型
```java
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
}
```
## 字典翻译
在后台字典管理中新增字典数据 参考前端代码views\system\menu\index.vue
使用步骤:
```javascript
- import { getDic } from '@/utils/auth'
- created() {
this.dicType = getDic('字典编码')
}
- formatterType({ cellValue }) {
const item = this.dicType.find(item => item.code === cellValue)
return item ? item.value : ''
}
-
```
## 异常处理
我们开发项目的时候,数据在请求过程中发生错误是非常常见的事情。
如:权限不足、数据唯一异常、数据不能为空异常、义务异常等。 这些异常如果不经过处理会对前端开发人员和使用者造成不便,因此我们就需要统一处理他们。
代码位置: cmdAdmin-common 工程中com.ninong.ker.common.exception包中
处理异常在每个模块 conf 包 LogAspect
### 异常封装
#### 异常实体
```java
/**
* 错误类
*
* @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,用于处理通用的异常
```java
/**
* @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 处理请求发送的异常
- @RestControllerAdvice:默认会扫描指定包中所有@RequestMapping 注解
- @ExceptionHandler:通过@ExceptionHandler 的 value 属性可过滤拦截的条件
```java
/\*\*
- 异常处理类
-
- @author cmd
- @date 2020 年 11 月 28 日 下午 11:22:48
\*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理所有不可知的异常
*/
@ExceptionHandler(Throwable.class)
public ResponseEntity handleException(Throwable e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
return buildResponseEntity(CmdError.error(e.getMessage()));
}
/**
* 处理自定义异常
*/
@ExceptionHandler(value = CmdException.class)
public ResponseEntity 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 methodArgumentNotValidException(MethodArgumentNotValidException e) {
List 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 buildResponseEntity(CmdError CmdError) {
return new ResponseEntity<>(CmdError, HttpStatus.valueOf(CmdError.getStatus()));
}
}
```
### 具体使用
```java
// 通用异常
throw new CmdException("系统提醒");
// 通用异常,使用自定义状态码
throw new CmdException(402, "系统提醒");
```
## 系统日志
本系统使用 AOP 方式记录用户操作日志,只需要在 controller 的方法上使用 @Log("") 注解,就可以将用户操作记录到数据库
模块具体使用如下:
```java
@Log("用户删除")
@CmdAdmin({"admin","user:delete"})
@DeleteMapping
public ResponseEntity delete(@RequestBody String[] ids) {
userService.delete(ids);
CmdResponse response = new CmdResponse();
response.setMessage(BusinessEnum.DELETE_SUCCESS.getMsg());
return ResponseEntity.ok(response);
}
```
页面日志中心可以看到 操作日志和异常日志









