我学习了ruoyi,终于知道怎么写数据权限

一、前言

这段时间也是一直在忙碌,每一天都不知道自己在做些什么,只是感觉自己越来越懒散,每一天下班回来什么都不想做,抱起ipad床上一躺,一晚上就过去了。

晚上9:00刚刚回到家,我坐在沙发上考虑我到底要做什么?为什么我没有一直坚持写Blog?我的生活难道只有工作吗?

考虑了十多分钟,我还是乖乖的打开电脑,看一看Ruoyi的代码,学习一下数据权限相关内容,下面就记录一下,我的学习过程以及相关思路。

二、基本原理

对于数据权限来说,如果没有看一些项目的实现,可能会不太好思考,我们首先来讲一讲什么是数权限:

数据权限

根据用户的岗位、角色等等,有区别的响应信息(这里是我个人的理解)

举个栗子:

需求:访问我发布的博客内容(我的用户id为101)

select 
   * from blog b 
left join user_blog ub on ub.blog_id = b.id
where ub.user_id = 101
复制代码

对于某一些系统来说,我们应该只被允许访问自己的被授权的内容,比如:普通员工只可以访问自己的数据的信息,部门经理可以访问整个部门的信息,财务部门负责人可以访问财务信息,我们并不希望普通员工可以直接获取到财务相关信息,也就是说专业人干专业事

实现的基本原理

上面讲了基本的概念

现在,我们需要思考,我们到底需要如何实现?对于基本的RBAC模型来说,我们可以划分为User、Role、Menu、User_Role、Role_Menu

用户绑定角色,角色绑定权限菜单,这样就可以根据角色判断用户是否有某一个功能的访问权限

But,上面所说的只是用户是否可以访问某一个资源,我们这里所说的是用户可以获取资源信息,但是需要根据不同的人,响应不同的信息,而不是不能访问

比如:

第一种: 普通用户访问管理员数据,这个是不允许

第二种: 普通用户访问整个部门的人员信息,这个可以访问,但是服务器之后响应普通用户一个人的信息,而不会把全部的数据响应

言归正传,步入正题:

我们需要创建User、Role、Dept、User_Role、Role_Dept四张表,具体如下:

image-20210727215653039.png

最值得关注的就是Role表中的data_scope,这个规定了不同角色的数据权限,可以访问的数据尺度,主要看下面一个sql语句:

 
       
   /*
      全部数据权限
   */
    select  u.*,d.*
    from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    left join sys_user_role ur on u.user_id = ur.user_id
    left join sys_role r on r.role_id = ur.role_id
    where u.del_flag = '0'
 
 /*
    自定义权限
    主要是根据sys_role_dept这张表的关联关系
 */
 select  u.*,d.*
    from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    left join sys_user_role ur on u.user_id = ur.user_id
    left join sys_role r on r.role_id = ur.role_id
    where u.del_flag = '0'
    //最需要观察的地方
    or d.dept_id IN ( 
      SELECT 
         dept_id 
      FROM sys_role_dept 
      WHERE role_id = 1 ) 
      

    /*
       部门数据权限
    */
   select  u.*,d.*
    from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    left join sys_user_role ur on u.user_id = ur.user_id
    left join sys_role r on r.role_id = ur.role_id
    where u.del_flag = '0'
    //最需要观察的地方
    or d.dept_id = 1
   
复制代码

大家看完上面的sql是否大概只要应该如何控制数据权限,最主要的就是where后面的条件,我们做简要分析:

**第一种:**全部权限

我们可以获取到所有未被标识删除的数据**(u.del_flag = '0')**,也就是可以获取除被删除以外的所有数据

**第二种:**自定义权限

自定义权限的话,最重要的一个地方就是role和dept的关联关系,普通用户如果被赋予财务会计部的权限,也是可以访问财务会计部的数据,增加了以下的条件:

 or d.dept_id IN ( 
      SELECT 
         dept_id 
      FROM sys_role_dept 
      WHERE role_id = 1 ) 
复制代码

第三种: 获取部门的数据权限

这个就是仅仅可以获取到所属部门的数据,他并不需要其他的关联关系,我们在图中可以看到User表中,已经存在dept_id,所以我们查询的时候只需要使用user.getDeptId()方法就可以获取

    or d.dept_id = 1
复制代码

上面讲了这么多,我们最后还需要思考一个问题:代码应该如何实现?

where语句我们并不能够**“写死”**,他应该是一个可变的,那么我们就需要将sql条件拼接上去,就可以完成

  • 判断用户关联的角色,获取角色的data_scope

    1、如果data_scope = 1 那么就不拼接任何的sql,直接查询所有非删除的数据

    2、如果data_scope = 2 那么就拼接,获取自定义的数据

     or d.dept_id IN ( 
          SELECT 
             dept_id 
          FROM sys_role_dept 
          WHERE role_id = 1 )
    复制代码

​ 3、如果data_scope 那么就拼接,获取部门的数据

    or d.dept_id = 1
复制代码

三、编写代码

(1)数据库

image-20210727215653039.png

User

create table sys_user (
  user_id           bigint(20)      not null auto_increment    comment '用户ID',
  dept_id           bigint(20)      default null               comment '部门ID',
  login_name        varchar(30)     not null                   comment '登录账号',
  user_name         varchar(30)     default ''                 comment '用户昵称',
  user_type         varchar(2)      default '00'               comment '用户类型(00系统用户 01注册用户)',
  email             varchar(50)     default ''                 comment '用户邮箱',
  phonenumber       varchar(11)     default ''                 comment '手机号码',
  sex               char(1)         default '0'                comment '用户性别(0男 1女 2未知)',
  avatar            varchar(100)    default ''                 comment '头像路径',
  password          varchar(50)     default ''                 comment '密码',
  salt              varchar(20)     default ''                 comment '盐加密',
  status            char(1)         default '0'                comment '帐号状态(0正常 1停用)',
  del_flag          char(1)         default '0'                comment '删除标志(0代表存在 2代表删除)',
  login_ip          varchar(50)     default ''                 comment '最后登陆IP',
  login_date        datetime                                   comment '最后登陆时间',
  create_by         varchar(64)     default ''                 comment '创建者',
  create_time       datetime                                   comment '创建时间',
  update_by         varchar(64)     default ''                 comment '更新者',
  update_time       datetime                                   comment '更新时间',
  remark            varchar(500)    default null               comment '备注',
  primary key (user_id)
) engine=innodb auto_increment=100 comment = '用户信息表';

insert into sys_user values(1,  100, 'admin', 'Gofly', '00', 'gofly@163.com', '15888888888', '1', '', '29c67a30398638269fe600f73a054934', '111111', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员');
insert into sys_user values(2,  101, 'kjb', 'kjb', '00', 'gofly@163.com', '15888888888', '1', '', '29c67a30398638269fe600f73a054934', '111111', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '部门负责人');

复制代码

Role

create table sys_role (
  role_id           bigint(20)      not null auto_increment    comment '角色ID',
  role_name         varchar(30)     not null                   comment '角色名称',
  role_key          varchar(100)    not null                   comment '角色权限字符串',
  role_sort         int(4)          not null                   comment '显示顺序',
  data_scope        char(1)         default '1'                comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
  status            char(1)         not null                   comment '角色状态(0正常 1停用)',
  del_flag          char(1)         default '0'                comment '删除标志(0代表存在 2代表删除)',
  create_by         varchar(64)     default ''                 comment '创建者',
  create_time       datetime                                   comment '创建时间',
  update_by         varchar(64)     default ''                 comment '更新者',
  update_time       datetime                                   comment '更新时间',
  remark            varchar(500)    default null               comment '备注',
  primary key (role_id)
) engine=innodb auto_increment=100 comment = '角色信息表';

insert into sys_role values('1', '超级管理员', 'admin',  1, 1, '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '超级管理员');
insert into sys_role values('2', '普通角色',   'common', 2, 3, '0', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '普通角色');
复制代码

Dept

create table sys_dept (
  dept_id           bigint(20)      not null auto_increment    comment '部门id',
  parent_id         bigint(20)      default 0                  comment '父部门id',
  ancestors         varchar(50)     default ''                 comment '祖级列表',
  dept_name         varchar(30)     default ''                 comment '部门名称',
  order_num         int(4)          default 0                  comment '显示顺序',
  leader            varchar(20)     default null               comment '负责人',
  phone             varchar(11)     default null               comment '联系电话',
  email             varchar(50)     default null               comment '邮箱',
  status            char(1)         default '0'                comment '部门状态(0正常 1停用)',
  del_flag          char(1)         default '0'                comment '删除标志(0代表存在 2代表删除)',
  create_by         varchar(64)     default ''                 comment '创建者',
  create_time 	    datetime                                   comment '创建时间',
  update_by         varchar(64)     default ''                 comment '更新者',
  update_time       datetime                                   comment '更新时间',
  primary key (dept_id)
) engine=innodb auto_increment=200 comment = '部门表';

insert into sys_dept values(100, 0, '0',  'Gofly科技', 0, 'Gofly', '15888888888', 'gofly@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'gofly', '2018-03-16 11-33-00');
insert into sys_dept values(101, 100, '0',  '科技部', 0, 'Gofly', '15888888888', 'gofly@qq.com', '0', '0', 'admin', '2018-03-16 11-33-00', 'gofly', '2018-03-16 11-33-00');
复制代码

user_role

create table sys_user_role (
  user_id   bigint(20) not null comment '用户ID',
  role_id   bigint(20) not null comment '角色ID',
  primary key(user_id, role_id)
) engine=innodb comment = '用户和角色关联表';

insert into sys_user_role values ('1', '1');
insert into sys_user_role values ('2', '2');
复制代码

role_dept

create table sys_role_dept (
  role_id   bigint(20) not null comment '角色ID',
  dept_id   bigint(20) not null comment '部门ID',
  primary key(role_id, dept_id)
) engine=innodb comment = '角色和部门关联表';

insert into sys_role_dept values ('1', '100');
insert into sys_role_dept values ('2', '101');
复制代码

(2)实体

我们上面提到,我们的sql语句,不能够“写死”,需要针对不同的用户角色设定不同的条件,那么 我们就需要定义一个BaseEntity专门用来存储sql的条件,其他的实体类只需要继承这个BaseEntity

public class BaseEntity implements Serializable {
    private Map<String,Object> params;

    public Map<String, Object> getParams() {
        if(params == null){
            params = new HashMap<>();
        }
        return params;
    }

    public void setParams(Map<String, Object> params) {
        this.params = params;
    }
}
复制代码

比如:我们获取部门的数据

getParams().put("dataScope","  or d.dept_id = 1")
复制代码

在mybatis xml中

${params.dataScope}


 select  u.*,d.*
    from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    left join sys_user_role ur on u.user_id = ur.user_id
    left join sys_role r on r.role_id = ur.role_id
    where u.del_flag = '0'
    //最需要观察的地方
    ${params.dataScope}

复制代码

User

@Data
public class SysUser extends BaseEntity implements Serializable {
    /**
     * 用户ID
     */
    private Long userId;

    /**
     * 部门ID
     */
    private Long deptId;

    /**
     * 登录账号
     */
    private String loginName;

    /**
     * 用户昵称
     */
    private String userName;

    /**
     * 用户类型(00系统用户 01注册用户)
     */
    private String userType;

    /**
     * 用户邮箱
     */
    private String email;

    /**
     * 手机号码
     */
    private String phonenumber;

    /**
     * 用户性别(0男 1女 2未知)
     */
    private String sex;

    /**
     * 头像路径
     */
    private String avatar;

    /**
     * 密码
     */
    private String password;

    /**
     * 盐加密
     */
    private String salt;

    /**
     * 帐号状态(0正常 1停用)
     */
    private String status;

    /**
     * 删除标志(0代表存在 2代表删除)
     */
    private String delFlag;

    /**
     * 最后登陆IP
     */
    private String loginIp;

    /**
     * 最后登陆时间
     */
    private Date loginDate;

    /**
     * 创建者
     */
    private String createBy;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 更新者
     */
    private String updateBy;

    /**
     * 更新时间
     */
    private Date updateTime;

    /**
     * 备注
     */
    private String remark;

    private static final long serialVersionUID = 1L;
}
复制代码

Dept

@Data
public class SysDept implements Serializable {
    /**
     * 部门id
     */
    private Long deptId;

    /**
     * 父部门id
     */
    private Long parentId;

    /**
     * 祖级列表
     */
    private String ancestors;

    /**
     * 部门名称
     */
    private String deptName;

    /**
     * 显示顺序
     */
    private Integer orderNum;

    /**
     * 负责人
     */
    private String leader;

    /**
     * 联系电话
     */
    private String phone;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 部门状态(0正常 1停用)
     */
    private String status;

    /**
     * 删除标志(0代表存在 2代表删除)
     */
    private String delFlag;

    /**
     * 创建者
     */
    private String createBy;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 更新者
     */
    private String updateBy;

    /**
     * 更新时间
     */
    private Date updateTime;

    private static final long serialVersionUID = 1L;
}
复制代码

Role

@Data
public class SysRole implements Serializable {
    /**
     * 角色ID
     */
    private Long roleId;

    /**
     * 角色名称
     */
    private String roleName;

    /**
     * 角色权限字符串
     */
    private String roleKey;

    /**
     * 显示顺序
     */
    private Integer roleSort;

    /**
     * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)
     */
    private String dataScope;

    /**
     * 角色状态(0正常 1停用)
     */
    private String status;

    /**
     * 删除标志(0代表存在 2代表删除)
     */
    private String delFlag;

    /**
     * 创建者
     */
    private String createBy;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 更新者
     */
    private String updateBy;

    /**
     * 更新时间
     */
    private Date updateTime;

    /**
     * 备注
     */
    private String remark;

    private static final long serialVersionUID = 1L;
}
复制代码

Dept_Role

@Data
public class SysRoleDeptKey implements Serializable {
    /**
     * 角色ID
     */
    private Long roleId;

    /**
     * 部门ID
     */
    private Long deptId;

    private static final long serialVersionUID = 1L;
}
复制代码

Role_User

@Data
public class SysUserRoleKey implements Serializable {
    /**
     * 用户ID
     */
    private Long userId;

    /**
     * 角色ID
     */
    private Long roleId;

    private static final long serialVersionUID = 1L;
}
复制代码

大家看完这个实体,可能会想一个问题:为什么只有SysUser实体继承BaseEntity,因为我们在这里只是测试获取用户列表,所以只有他继承

(3)注解

我们在设置数据权限中,我们需要自定义注解,一说到注解,大家肯定会想到AOP,我么自定义注解,然后使用AOP进行相关的操作

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope {

    /**
     * 部门别名
     * @return
     */
    public String deptAlias() default "";

    /**
     * 用户别名
     * @return
     */
    public String userAlias() default "";

}
复制代码

(4)AOP

这个就是今晚的主角,也是所有操作中的重点,因为他要实现相关sql的拼接工作

@Aspect
@Component
public class DataScopeAspectJ {

    @Autowired
    private IUserService iUserService;

    @Autowired
    private IRoleService iRoleService;

    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";

    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";

    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";

    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";

    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";

    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";


    @Pointcut("@annotation(com.yangzinan.datascopeproject.annotation.DataScope)")
    public void dataScopePointCut(){

    }

    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint joinPoint){
        doHandler(joinPoint);
    }

    public void doHandler(JoinPoint joinPoint){
        /**
         * 获取注解
         */
        DataScope dataScope = getAnnotationLog(joinPoint);

        if(dataScope == null){
            return ;
        }

        //这里模拟从shiro中获取用户信息
        SysUser sysUser = iUserService.selectUserById(1L);

        if(sysUser !=  null){
            //数据权限处理
            dataScopeHandler(sysUser,joinPoint,dataScope.deptAlias(),dataScope.userAlias());
        }

    }

    /**
     * 数据权限处理
     * @param user
     * @param joinPoint
     * @param deptAlias
     * @param userAlias
     */
    public void dataScopeHandler(SysUser user,JoinPoint joinPoint,String deptAlias,String userAlias){
        //用来拼接字符串
        StringBuilder stringBuilder = new StringBuilder();

        //根据用户id获取用户所有的角色
        List<SysRole> sysRoles = iRoleService.selectRolesByUserId(user.getUserId());

        for(SysRole role : sysRoles){

            //DATA_SCOPE_ALL = 1 获取所有的数据
            if(role.getDataScope().equals(DATA_SCOPE_ALL)){
                stringBuilder  = new StringBuilder();
                break;
            }
            //DATA_SCOPE_ALL = 2 获取自定义的数据
            else if(role.getDataScope().equals(DATA_SCOPE_CUSTOM)){
                stringBuilder.append(String.format(
                        " OR %s.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = %s ) ", deptAlias,
                        role.getRoleId()));
            }

            //DATA_SCOPE_ALL = 3 获取部门的数据
            else if(role.getDataScope().equals(DATA_SCOPE_DEPT)){
                stringBuilder.append(String.format(
                        " OR %s.dept_id = %s ", deptAlias, user.getDeptId()));
            }

            //DATA_SCOPE_ALL = 4 获取部门及以下的数据
            else if(role.getDataScope().equals(DATA_SCOPE_DEPT_AND_CHILD)){
                stringBuilder.append(String.format(
                        " OR %s.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = %s or find_in_set( %s , ancestors ) )",
                        deptAlias, user.getDeptId(), user.getDeptId()));
            }
        }


        /**
         * 如果StringBuilder不为空,说明上面有拼接的sql,将它放到BaseEntity中的Map中
         */
            if (StringUtils.isNotBlank(stringBuilder.toString()))
            {
                Object params = joinPoint.getArgs()[0];
                if (!org.springframework.util.StringUtils.isEmpty(params) && params instanceof BaseEntity)
                {
                    BaseEntity baseEntity = (BaseEntity) params;
                    baseEntity.getParams().put(DATA_SCOPE, " AND (" + stringBuilder.substring(4) + ")");
                }
            }



    }

    /**
     * 获取注解
     * @param joinPoint
     * @return
     */
    private DataScope getAnnotationLog(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if(method != null){
            return method.getAnnotation(DataScope.class);
        }

        return null;
    }

}
复制代码

(5)Mybatis xml

  <select id="selectUsers" parameterType="com.yangzinan.datascopeproject.entity.SysUser" resultMap="BaseResultMap">
    select  u.*,d.*
    from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    left join sys_user_role ur on u.user_id = ur.user_id
    left join sys_role r on r.role_id = ur.role_id
    where u.del_flag = '0'
    <!--数据权限-->
    ${params.dataScope}
  </select>
复制代码

(6)Mapper

@Mapper
public interface SysUserMapper {
    int deleteByPrimaryKey(Long userId);

    int insert(SysUser record);

    int insertSelective(SysUser record);

    SysUser selectByPrimaryKey(Long userId);

    int updateByPrimaryKeySelective(SysUser record);

    int updateByPrimaryKey(SysUser record);

    List<SysUser> selectUserList();
		
  	/*
  	    重点
  	*/
    List<SysUser> selectUsers(SysUser sysUser);
}
复制代码

(7)Service

这里需要注意,我们将自定义的注解加到这里

@Service
public class UserServiceImpl  implements IUserService{

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public List<SysUser> selectUserList() {
        return sysUserMapper.selectUserList();
    }

    @Override
    public SysUser selectUserById(Long userId) {
        return sysUserMapper.selectByPrimaryKey(userId);
    }

  /*
    重点
  */
    @DataScope(deptAlias = "d", userAlias = "u")
    @Override
    public List<SysUser> selectUsers(SysUser sysUser) {
        return sysUserMapper.selectUsers(sysUser);
    }
}
复制代码

(8)Controller

@RestController
public class UserController {

    @Autowired
    private IUserService userService;

    @GetMapping("list")
    public List<SysUser> getUserList(){
        SysUser sysUser = new SysUser();
        return userService.selectUsers(sysUser);
    }

}
复制代码

(9)测试

第一种:管理员获取数据

我们主要修改AOP中的以下内容:

  • 用户id为 1L:管理员 数据权限为获取所有用户信息
  • 用户id为 2L:普通用户 数据权限为获取部门用户信息
//这里模拟从shiro中获取用户信息(管理员)
SysUser sysUser = iUserService.selectUserById(1L);
复制代码
[{
	"params": {},
	"userId": 1,
	"deptId": 100,
	"loginName": "admin",
	"userName": "Gofly",
	"userType": "00",
	"email": "gofly@163.com",
	"phonenumber": "15888888888",
	"sex": "1",
	"avatar": "",
	"password": "29c67a30398638269fe600f73a054934",
	"salt": "111111",
	"status": "0",
	"delFlag": "0",
	"loginIp": "127.0.0.1",
	"loginDate": "2018-03-16T11:33:00.000+00:00",
	"createBy": "admin",
	"createTime": "2018-03-16T11:33:00.000+00:00",
	"updateBy": "ry",
	"updateTime": "2018-03-16T11:33:00.000+00:00",
	"remark": "管理员"
}, {
	"params": {},
	"userId": 2,
	"deptId": 101,
	"loginName": "kjb",
	"userName": "kjb",
	"userType": "00",
	"email": "gofly@163.com",
	"phonenumber": "15888888888",
	"sex": "1",
	"avatar": "",
	"password": "29c67a30398638269fe600f73a054934",
	"salt": "111111",
	"status": "0",
	"delFlag": "0",
	"loginIp": "127.0.0.1",
	"loginDate": "2018-03-16T11:33:00.000+00:00",
	"createBy": "admin",
	"createTime": "2018-03-16T11:33:00.000+00:00",
	"updateBy": "ry",
	"updateTime": "2018-03-16T11:33:00.000+00:00",
	"remark": "部门负责人"
}]
复制代码

第二种:普通用户获取数据

修改以下:

//这里模拟从shiro中获取用户信息(管理员)
SysUser sysUser = iUserService.selectUserById(1L);
复制代码
[{
	"params": {},
	"userId": 2,
	"deptId": 101,
	"loginName": "kjb",
	"userName": "kjb",
	"userType": "00",
	"email": "gofly@163.com",
	"phonenumber": "15888888888",
	"sex": "1",
	"avatar": "",
	"password": "29c67a30398638269fe600f73a054934",
	"salt": "111111",
	"status": "0",
	"delFlag": "0",
	"loginIp": "127.0.0.1",
	"loginDate": "2018-03-16T11:33:00.000+00:00",
	"createBy": "admin",
	"createTime": "2018-03-16T11:33:00.000+00:00",
	"updateBy": "ry",
	"updateTime": "2018-03-16T11:33:00.000+00:00",
	"remark": "部门负责人"
}]
复制代码

今天就写到这里吧!等明天起床我再好好的复盘,不断去修改和完善内容,如果写不对的地方,还请见谅!

晚安🌛