提交 d3ebeeea authored 作者: 李秋林's avatar 李秋林

定时任务同步飞书部门、用户、部门负责人等信息

上级 13288fc7
...@@ -83,6 +83,41 @@ ...@@ -83,6 +83,41 @@
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.larksuite.oapi</groupId>
<artifactId>oapi-sdk</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -14,7 +14,7 @@ import com.sfa.common.security.annotation.EnableRyFeignClients; ...@@ -14,7 +14,7 @@ import com.sfa.common.security.annotation.EnableRyFeignClients;
@EnableCustomConfig @EnableCustomConfig
@EnableRyFeignClients @EnableRyFeignClients
@SpringBootApplication @SpringBootApplication
@MapperScan("com.sfa.job.mapper") @MapperScan({"com.sfa.job.domain.job.mapper","com.sfa.job.domain.system.mapper"})
public class SfaJobApplication public class SfaJobApplication
{ {
public static void main(String[] args) { public static void main(String[] args) {
......
package com.sfa.job.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl-job.admin.addresses}")
private String adminAddresses;
@Value("${xxl-job.accessToken}")
private String accessToken;
@Value("${xxl-job.executor.appname}")
private String appname;
@Value("${xxl-job.executor.address}")
private String address;
@Value("${xxl-job.executor.ip}")
private String ip;
@Value("${xxl-job.executor.port}")
private int port;
@Value("${xxl-job.executor.logpath}")
private String logPath;
@Value("${xxl-job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
/**
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
*
* 1、引入依赖:
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-commons</artifactId>
* <version>${version}</version>
* </dependency>
*
* 2、配置文件,或者容器启动变量
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、获取IP
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}
\ No newline at end of file
package com.sfa.job.controller; package com.sfa.job.controller.job;
import com.sfa.common.core.constant.Constants; import com.sfa.common.core.constant.Constants;
import com.sfa.common.core.exception.job.TaskException; import com.sfa.common.core.exception.job.TaskException;
...@@ -11,8 +11,8 @@ import com.sfa.common.log.annotation.Log; ...@@ -11,8 +11,8 @@ import com.sfa.common.log.annotation.Log;
import com.sfa.common.log.enums.BusinessType; import com.sfa.common.log.enums.BusinessType;
import com.sfa.common.security.annotation.RequiresPermissions; import com.sfa.common.security.annotation.RequiresPermissions;
import com.sfa.common.security.utils.SecurityUtils; import com.sfa.common.security.utils.SecurityUtils;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
import com.sfa.job.service.ISysJobService; import com.sfa.job.service.job.ISysJobService;
import com.sfa.job.util.CronUtils; import com.sfa.job.util.CronUtils;
import com.sfa.job.util.ScheduleUtils; import com.sfa.job.util.ScheduleUtils;
import org.quartz.SchedulerException; import org.quartz.SchedulerException;
......
package com.sfa.job.controller; package com.sfa.job.controller.job;
import com.sfa.common.core.utils.poi.ExcelUtil; import com.sfa.common.core.utils.poi.ExcelUtil;
import com.sfa.common.core.web.controller.BaseController; import com.sfa.common.core.web.controller.BaseController;
...@@ -7,8 +7,8 @@ import com.sfa.common.core.web.domain.PageInfo; ...@@ -7,8 +7,8 @@ import com.sfa.common.core.web.domain.PageInfo;
import com.sfa.common.log.annotation.Log; import com.sfa.common.log.annotation.Log;
import com.sfa.common.log.enums.BusinessType; import com.sfa.common.log.enums.BusinessType;
import com.sfa.common.security.annotation.RequiresPermissions; import com.sfa.common.security.annotation.RequiresPermissions;
import com.sfa.job.domain.SysJobLog; import com.sfa.job.domain.job.entity.SysJobLog;
import com.sfa.job.service.ISysJobLogService; import com.sfa.job.service.job.ISysJobLogService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
......
package com.sfa.job.domain; package com.sfa.job.domain.job.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
......
package com.sfa.job.domain; package com.sfa.job.domain.job.entity;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
......
package com.sfa.job.mapper; package com.sfa.job.domain.job.mapper;
import java.util.List; import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sfa.job.domain.SysJobLog; import com.sfa.job.domain.job.entity.SysJobLog;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
......
package com.sfa.job.mapper; package com.sfa.job.domain.job.mapper;
import java.util.List; import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
......
package com.sfa.job.domain.system.dao;
import com.alibaba.fastjson2.JSONObject;
import com.sfa.job.domain.system.entity.SysDept;
import java.util.List;
/**
* @author : liqiulin
* @date : 2024-12-09 13
* @describe :
*/
public interface ISysDeptDao {
SysDept insertOrUpdate(JSONObject jo,SysDept parentDept);
SysDept selectByDeptCode(String deptCode);
List<SysDept> selectListByStatus();
}
package com.sfa.job.domain.system.dao;
import com.alibaba.fastjson2.JSONObject;
/**
* @author : liqiulin
* @date : 2024-12-10 14
* @describe :
*/
public interface ISysUserDao {
void insertOrUpdate(JSONObject jo,Long deptId);
void updateDeptLeader();
}
package com.sfa.job.domain.system.dao;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sfa.common.core.enums.StatusType;
import com.sfa.job.domain.system.entity.SysDept;
import com.sfa.job.domain.system.mapper.SysDeptMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* @author : liqiulin
* @date : 2024-12-09 13
* @describe :
*/
@Service
public class SysDeptDaoImpl implements ISysDeptDao{
@Autowired
SysDeptMapper sysDeptMapper;
/**
* 插入或更新部门信息
*/
@Override
public SysDept insertOrUpdate(JSONObject jo,SysDept parentDept) {
SysDept sysDept = new SysDept();
sysDept.setCreateBy("admin");
sysDept.setCreateUserId(1L);
sysDept.setUpdateBy("admin");
sysDept.setUpdateUserId(1L);
String departmentId = jo.getString("department_id");
sysDept.setDeptCode(departmentId);
String name = jo.getString("name");
sysDept.setDeptName(name);
String openDepartmentId = jo.getString("open_department_id");
sysDept.setFsDeptId(openDepartmentId);
sysDept.setParentId(parentDept.getDeptId());
sysDept.setParentCode(parentDept.getDeptCode());
sysDept.setParentName(parentDept.getDeptName());
sysDept.setParentFsId(parentDept.getFsDeptId());
if (StringUtils.isBlank(parentDept.getAncestors())){
sysDept.setAncestors(parentDept.getDeptId()+"");
}else {
sysDept.setAncestors(parentDept.getAncestors()+","+parentDept.getDeptId());
}
if (StringUtils.isBlank(parentDept.getAncestorNames())){
sysDept.setAncestorNames(parentDept.getDeptName()+"");
}else {
sysDept.setAncestorNames(parentDept.getAncestorNames()+","+parentDept.getDeptName());
}
String order = jo.getString("order");
sysDept.setOrderNum(Integer.parseInt(order));
if (jo.containsKey("leaders")){
Map leader = (Map) jo.getJSONArray("leaders").get(0);
sysDept.setFsLeaderId(leader.get("leaderID").toString());
}
Integer memberCount = jo.getInteger("primary_member_count");
sysDept.setMemberCount(memberCount);
Boolean statusB = jo.getJSONObject("status").getBoolean("is_deleted");
sysDept.setStatus(statusB?"1":"0");
sysDeptMapper.insertOrUpdate(sysDept);
return sysDept;
}
/**
* 根据部门编码查询部门信息
* @param deptCode 0时默认查询根部门信息
*/
@Override
public SysDept selectByDeptCode(String deptCode) {
SysDept sysDept = null;
if ("0".equals(deptCode)){
sysDept = sysDeptMapper.selectById(1);
}else {
sysDept = sysDeptMapper.selectOne(new LambdaQueryWrapper<SysDept>().eq(SysDept::getDeptCode, deptCode).eq(SysDept::getStatus, StatusType.VALID.getType() + ""));
}
return sysDept;
}
/**
* 查询有效的部门列表
*/
@Override
public List<SysDept> selectListByStatus() {
return sysDeptMapper.selectListByStatus();
}
}
package com.sfa.job.domain.system.dao;
import com.alibaba.fastjson2.JSONObject;
import com.sfa.common.core.enums.StatusType;
import com.sfa.job.domain.system.entity.SysUser;
import com.sfa.job.domain.system.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author : liqiulin
* @date : 2024-12-10 14
* @describe :
*/
@Service
public class SysUserDaoImpl implements ISysUserDao{
@Autowired
private SysUserMapper sysUserMapper;
@Override
public void insertOrUpdate(JSONObject jo, Long deptId) {
SysUser sysUser = new SysUser();
sysUser.setCreateBy("admin");
sysUser.setCreateUserId(1L);
sysUser.setUpdateBy("admin");
sysUser.setUpdateUserId(1L);
sysUser.setDeptId(deptId);
sysUser.setUserName(jo.getString("employee_no"));
sysUser.setNickName(jo.getString("name"));
sysUser.setUserType("00");
sysUser.setFsUserId(jo.getString("user_id"));
sysUser.setFsOpenId(jo.getString("open_id"));
sysUser.setEmail(jo.getString("enterprise_email"));
sysUser.setPhonenumber(jo.getString("mobile").replace("+86",""));
sysUser.setSex("2");
sysUser.setAvatar("");
sysUser.setPassword("");
sysUser.setRoleId(0L);
sysUser.setRoleName("");
sysUser.setWorkCityName(jo.getString("city"));
sysUser.setStatus(StatusType.INVALID.getType()+"");
sysUserMapper.insertOrUpdate(sysUser);
}
@Override
public void updateDeptLeader() {
sysUserMapper.updateDeptLeader();
}
}
package com.sfa.job.domain.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* 部门表
* @TableName sys_dept
*/
@TableName(value ="sys_dept")
@Data
public class SysDept implements Serializable {
/**
* 部门id
*/
@TableId(type = IdType.AUTO)
private Long deptId;
/**
* 部门编号(飞书-department_id)
*/
private String deptCode;
/**
* 部门名称
*/
private String deptName;
/**
* 飞书部门IDopen_department_id
*/
private String fsDeptId;
/**
* 父部门id
*/
private Long parentId;
/**
* 父部门编号
*/
private String parentCode;
/**
* 父部门名称
*/
private String parentName;
/**
* 父部门飞书部门ID
*/
private String parentFsId;
/**
* 祖级id列表
*/
private String ancestors;
/**
* 部门编码Path
*/
private String ancestorCodes;
/**
* 部门名称Path
*/
private String ancestorNames;
/**
* 显示顺序
*/
private Integer orderNum;
/**
* 负责人姓名
*/
private String leader;
/**
* 飞书负责人id
*/
private String fsLeaderId;
/**
* 联系电话
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 部门人数
*/
private Integer memberCount;
/**
* 部门状态(0正常 1停用)
*/
private String status;
/**
* 删除标志(0代表存在 2代表删除)
*/
private String delFlag;
/**
* 创建者
*/
private String createBy;
/**
* 创建人UserID
*/
private Long createUserId;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 修改人UserID
*/
private Long updateUserId;
/**
* 更新时间
*/
private Date updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
\ No newline at end of file
package com.sfa.job.domain.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* 用户信息表
* @TableName sys_user
*/
@TableName(value ="sys_user")
@Data
public class SysUser implements Serializable {
/**
* 用户ID
*/
@TableId(type = IdType.AUTO)
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户登录账号
*/
private String userName;
/**
* 用户昵称
*/
private String nickName;
/**
* 用户类型(00系统用户)
*/
private String userType;
/**
* 飞书-用户id
*/
private String fsUserId;
private String fsOpenId;
/**
* 用户邮箱
*/
private String email;
/**
* 手机号码
*/
private String phonenumber;
/**
* 用户性别(0男 1女 2未知)
*/
private String sex;
/**
* 头像地址
*/
private String avatar;
/**
* 密码
*/
private String password;
/**
* 角色ID
*/
private Long roleId;
/**
* 角色名称
*/
private String roleName;
/**
* 岗位ID
*/
private Long postId;
/**
* 岗位名称
*/
private String postName;
/**
* 考勤规则ID
*/
private Long ruleId;
/**
* 考勤规则名称
*/
private String ruleName;
/**
* 工作-省编码
*/
private String workProvinceNum;
/**
* 工作-省编码
*/
private String workProvinceName;
/**
* 工作-市编码
*/
private String workCityNum;
/**
* 工作-市名称
*/
private String workCityName;
/**
* 帐号状态(0正常 1停用)
*/
private String status;
/**
* 删除标志(0代表存在 2代表删除)
*/
private String delFlag;
/**
* 最后登录IP
*/
private String loginIp;
/**
* 最后登录时间
*/
private Date loginDate;
/**
* 创建者
*/
private String createBy;
/**
* 创建人UserID
*/
private Long createUserId;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 修改人UserID
*/
private Long updateUserId;
/**
* 更新时间
*/
private Date updateTime;
/**
* 备注
*/
private String remark;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
\ No newline at end of file
package com.sfa.job.domain.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sfa.job.domain.system.entity.SysDept;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author a02200059
* @description 针对表【sys_dept(部门表)】的数据库操作Mapper
* @createDate 2024-12-06 16:04:49
* @Entity com.sfa.job.domain.system.entity.SysDept
*/
@Repository
public interface SysDeptMapper extends BaseMapper<SysDept> {
void insertOrUpdate(SysDept sysDept);
List<SysDept> selectListByStatus();
}
package com.sfa.job.domain.system.mapper;
import com.sfa.job.domain.system.entity.SysUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* @author a02200059
* @description 针对表【sys_user(用户信息表)】的数据库操作Mapper
* @createDate 2024-12-10 14:44:46
* @Entity com.sfa.job.domain.system.entity.SysUser
*/
@Repository
public interface SysUserMapper extends BaseMapper<SysUser> {
void insertOrUpdate(SysUser sysUser);
void updateDeptLeader();
}
package com.sfa.job.service; package com.sfa.job.service.job;
import com.sfa.common.core.web.domain.PageInfo; import com.sfa.common.core.web.domain.PageInfo;
import com.sfa.job.domain.SysJobLog; import com.sfa.job.domain.job.entity.SysJobLog;
/** /**
* 定时任务调度日志信息信息 服务层 * 定时任务调度日志信息信息 服务层
......
package com.sfa.job.service; package com.sfa.job.service.job;
import com.sfa.common.core.exception.job.TaskException; import com.sfa.common.core.exception.job.TaskException;
import com.sfa.common.core.web.domain.PageInfo; import com.sfa.common.core.web.domain.PageInfo;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
import org.quartz.SchedulerException; import org.quartz.SchedulerException;
/** /**
......
package com.sfa.job.service; package com.sfa.job.service.job;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sfa.common.core.web.domain.PageInfo; import com.sfa.common.core.web.domain.PageInfo;
import com.sfa.common.core.web.page.TableSupport; import com.sfa.common.core.web.page.TableSupport;
import com.sfa.job.domain.SysJobLog; import com.sfa.job.domain.job.entity.SysJobLog;
import com.sfa.job.mapper.SysJobLogMapper; import com.sfa.job.domain.job.mapper.SysJobLogMapper;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
......
package com.sfa.job.service; package com.sfa.job.service.job;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
...@@ -6,8 +6,8 @@ import com.sfa.common.core.constant.ScheduleConstants; ...@@ -6,8 +6,8 @@ import com.sfa.common.core.constant.ScheduleConstants;
import com.sfa.common.core.exception.job.TaskException; import com.sfa.common.core.exception.job.TaskException;
import com.sfa.common.core.web.domain.PageInfo; import com.sfa.common.core.web.domain.PageInfo;
import com.sfa.common.core.web.page.TableSupport; import com.sfa.common.core.web.page.TableSupport;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
import com.sfa.job.mapper.SysJobMapper; import com.sfa.job.domain.job.mapper.SysJobMapper;
import com.sfa.job.util.CronUtils; import com.sfa.job.util.CronUtils;
import com.sfa.job.util.ScheduleUtils; import com.sfa.job.util.ScheduleUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
......
package com.sfa.job.service.system;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.sfa.job.domain.system.dao.ISysDeptDao;
import com.sfa.job.domain.system.dao.ISysUserDao;
import com.sfa.job.domain.system.entity.SysDept;
import com.sfa.job.util.FeiShuUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author : liqiulin
* @date : 2024-12-06 18
* @describe :
*/
@Slf4j
@Service
public class DeptAndUserServiceImpl implements IDeptAndUserService {
@Autowired
private FeiShuUtil feiShuUtil;
@Autowired
private ISysDeptDao sysDeptDao;
@Autowired
private ISysUserDao sysUserDao;
/**
* 从飞书同步全量部门信息
*/
@Override
public void deptAll() {
// 根部门编码
String cDeptId = "0";
JSONArray depts = feiShuUtil.getDeptByParentId(cDeptId);
SysDept sysDept = sysDeptDao.selectByDeptCode(cDeptId);
for (Object dept : depts){
SysDept sysDept2 = insertOrUpdateDept(dept, sysDept);
deptSub(sysDept2.getDeptCode());
}
}
/**
* 从飞书同步用户信息
* 按部门同步
*/
@Override
public void userAll() {
// 查询所有有效部门,deptCode对应飞书deptId
List<SysDept> depts = sysDeptDao.selectListByStatus();
for (SysDept dept : depts) {
JSONArray users = feiShuUtil.getUsersByDeptId(dept.getDeptCode());
if (users == null || users.size() == 0){
log.info("{}部门暂无员工;",dept.getDeptName());
continue;
}
insertOrUpdateUsers(users,dept.getDeptId());
}
sysUserDao.updateDeptLeader();
}
/**
* 同步二级以下部门
*/
private void deptSub(String parentDeptCode) {
JSONArray depts = feiShuUtil.getDeptByParentId(parentDeptCode);
if (depts == null || depts.size() == 0){
return;
}
SysDept sysDept = sysDeptDao.selectByDeptCode(parentDeptCode);
// 遍历部门
for (Object dept : depts){
SysDept sysDept2 = insertOrUpdateDept(dept, sysDept);
deptSub(sysDept2.getDeptCode());
}
}
/**
* 新增或更新部门
*/
private SysDept insertOrUpdateDept(Object dept,SysDept parentDept) {
JSONObject jo = JSONObject.parseObject(JSONObject.toJSONString(dept));
return sysDeptDao.insertOrUpdate(jo,parentDept);
}
private void insertOrUpdateUsers(JSONArray users,Long deptId){
for (Object user : users) {
JSONObject jo = JSONObject.parseObject(JSONObject.toJSONString(user));
sysUserDao.insertOrUpdate(jo,deptId);
}
}
}
package com.sfa.job.service.system;
/**
* @author : liqiulin
* @date : 2024-12-06 18
* @describe :
*/
public interface IDeptAndUserService {
void deptAll();
void userAll();
}
...@@ -11,9 +11,9 @@ import com.sfa.common.core.utils.ExceptionUtil; ...@@ -11,9 +11,9 @@ import com.sfa.common.core.utils.ExceptionUtil;
import com.sfa.common.core.utils.SpringUtils; import com.sfa.common.core.utils.SpringUtils;
import com.sfa.common.core.utils.StringUtils; import com.sfa.common.core.utils.StringUtils;
import com.sfa.common.core.utils.bean.BeanUtils; import com.sfa.common.core.utils.bean.BeanUtils;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
import com.sfa.job.domain.SysJobLog; import com.sfa.job.domain.job.entity.SysJobLog;
import com.sfa.job.service.ISysJobLogService; import com.sfa.job.service.job.ISysJobLogService;
/** /**
* 抽象quartz调用 * 抽象quartz调用
......
package com.sfa.job.util;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.gson.JsonParser;
import com.lark.oapi.Client;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.service.contact.v3.enums.ChildrenDepartmentDepartmentIdTypeEnum;
import com.lark.oapi.service.contact.v3.enums.FindByDepartmentUserDepartmentIdTypeEnum;
import com.lark.oapi.service.contact.v3.enums.FindByDepartmentUserUserIdTypeEnum;
import com.lark.oapi.service.contact.v3.model.ChildrenDepartmentReq;
import com.lark.oapi.service.contact.v3.model.ChildrenDepartmentResp;
import com.lark.oapi.service.contact.v3.model.FindByDepartmentUserReq;
import com.lark.oapi.service.contact.v3.model.FindByDepartmentUserResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author : liqiulin
* @date : 2024-12-06 16
* @describe :
*/
@Slf4j
@Component
public class FeiShuUtil {
/**
* 根据部门父编码获取子部门
* 公司编码:"0"
*/
public JSONArray getDeptByParentId(String parentId) {
try {
Client client = getClient();
ChildrenDepartmentReq req = ChildrenDepartmentReq.newBuilder()
.departmentId(parentId)
.departmentIdType(ChildrenDepartmentDepartmentIdTypeEnum.DEPARTMENT_ID)
.pageSize(50)
.build();
ChildrenDepartmentResp resp = client.contact().department().children(req);
if (!resp.success()) {
log.error(String.format("code:%s,msg:%s,reqId:%s, resp:%s",
resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), "UTF-8")))));
return null;
}
JSONArray items = JSONObject.parse(Jsons.DEFAULT.toJson(resp.getData())).getJSONArray("items");
return items;
} catch (Exception e) {
log.error("获取子部门列表失败,停止执行!");
}
return null;
}
public JSONArray getUsersByDeptId(String deptCode) {
try {
Client client = getClient();
FindByDepartmentUserReq req = FindByDepartmentUserReq.newBuilder()
.userIdType(FindByDepartmentUserUserIdTypeEnum.OPEN_ID)
.departmentIdType(FindByDepartmentUserDepartmentIdTypeEnum.DEPARTMENT_ID)
.departmentId(deptCode)
.pageSize(50)
.build();
FindByDepartmentUserResp resp = client.contact().user().findByDepartment(req);
if(!resp.success()) {
System.out.println(String.format("code:%s,msg:%s,reqId:%s, resp:%s",
resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), "UTF-8")))));
return null;
}
JSONArray items = JSONObject.parse(Jsons.DEFAULT.toJson(resp.getData())).getJSONArray("items");
return items;
} catch (Exception e) {
log.error("获取部门用户失败,停止执行!部门id:{}", deptCode);
}
return null;
}
private Client getClient() {
return Client.newBuilder("cli_a7cd598c6d46100b", "gkLZNITiY7UNUCMdyhy1CdMXavj6cmhZ").build();
}
}
\ No newline at end of file
...@@ -6,7 +6,7 @@ import java.util.LinkedList; ...@@ -6,7 +6,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import com.sfa.common.core.utils.SpringUtils; import com.sfa.common.core.utils.SpringUtils;
import com.sfa.common.core.utils.StringUtils; import com.sfa.common.core.utils.StringUtils;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
/** /**
* 任务执行工具 * 任务执行工具
......
...@@ -3,7 +3,7 @@ package com.sfa.job.util; ...@@ -3,7 +3,7 @@ package com.sfa.job.util;
import org.quartz.DisallowConcurrentExecution; import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext; import org.quartz.JobExecutionContext;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
/** /**
* 定时任务处理(禁止并发执行) * 定时任务处理(禁止并发执行)
......
...@@ -2,7 +2,7 @@ package com.sfa.job.util; ...@@ -2,7 +2,7 @@ package com.sfa.job.util;
import org.quartz.JobExecutionContext; import org.quartz.JobExecutionContext;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
/** /**
* 定时任务处理(允许并发执行) * 定时任务处理(允许并发执行)
......
...@@ -16,7 +16,7 @@ import com.sfa.common.core.exception.job.TaskException; ...@@ -16,7 +16,7 @@ import com.sfa.common.core.exception.job.TaskException;
import com.sfa.common.core.exception.job.TaskException.Code; import com.sfa.common.core.exception.job.TaskException.Code;
import com.sfa.common.core.utils.SpringUtils; import com.sfa.common.core.utils.SpringUtils;
import com.sfa.common.core.utils.StringUtils; import com.sfa.common.core.utils.StringUtils;
import com.sfa.job.domain.SysJob; import com.sfa.job.domain.job.entity.SysJob;
/** /**
* 定时任务工具类 * 定时任务工具类
......
package com.sfa.job.xxljob;
import com.sfa.job.service.system.IDeptAndUserService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author : liqiulin
* @date : 2024-12-10 16
* @describe :系统数据任务
*/
@Component
@Slf4j
public class SystemTask {
@Autowired
IDeptAndUserService deptAndUserService;
/**
* 同步部门数据
*/
@XxlJob("sync_feishu_dept")
public void deptAll() {
log.info("开始同步部门数据");
deptAndUserService.deptAll();
log.info("结束同步部门数据");
}
/**
* 同步人员
*/
@XxlJob("sync_feishu_user")
public void userAll() {
log.info("开始同步飞书用户数据");
deptAndUserService.userAll();
log.info("结束同步飞书用户数据");
}
}
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sfa.job.mapper.SysJobLogMapper"> <mapper namespace="com.sfa.job.domain.job.mapper.SysJobLogMapper">
<resultMap type="com.sfa.job.domain.SysJobLog" id="SysJobLogResult"> <resultMap type="com.sfa.job.domain.job.entity.SysJobLog" id="SysJobLogResult">
<id property="jobLogId" column="job_log_id" /> <id property="jobLogId" column="job_log_id" />
<result property="jobName" column="job_name" /> <result property="jobName" column="job_name" />
<result property="jobGroup" column="job_group" /> <result property="jobGroup" column="job_group" />
...@@ -20,7 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -20,7 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from sys_job_log from sys_job_log
</sql> </sql>
<select id="selectJobLogList" parameterType="com.sfa.job.domain.SysJobLog" resultMap="SysJobLogResult"> <select id="selectJobLogList" parameterType="com.sfa.job.domain.job.entity.SysJobLog" resultMap="SysJobLogResult">
<include refid="selectJobLogVo"/> <include refid="selectJobLogVo"/>
<where> <where>
<if test="jobName != null and jobName != ''"> <if test="jobName != null and jobName != ''">
...@@ -63,7 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -63,7 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
truncate table sys_job_log truncate table sys_job_log
</update> </update>
<insert id="insertJobLog" parameterType="com.sfa.job.domain.SysJobLog"> <insert id="insertJobLog" parameterType="com.sfa.job.domain.job.entity.SysJobLog">
insert into sys_job_log( insert into sys_job_log(
<if test="jobLogId != null and jobLogId != 0">job_log_id,</if> <if test="jobLogId != null and jobLogId != 0">job_log_id,</if>
<if test="jobName != null and jobName != ''">job_name,</if> <if test="jobName != null and jobName != ''">job_name,</if>
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sfa.job.mapper.SysJobMapper"> <mapper namespace="com.sfa.job.domain.job.mapper.SysJobMapper">
<resultMap type="com.sfa.job.domain.SysJob" id="SysJobResult"> <resultMap type="com.sfa.job.domain.job.entity.SysJob" id="SysJobResult">
<id property="jobId" column="job_id" /> <id property="jobId" column="job_id" />
<result property="jobName" column="job_name" /> <result property="jobName" column="job_name" />
<result property="jobGroup" column="job_group" /> <result property="jobGroup" column="job_group" />
...@@ -25,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -25,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from sys_job from sys_job
</sql> </sql>
<select id="selectJobList" parameterType="com.sfa.job.domain.SysJob" resultMap="SysJobResult"> <select id="selectJobList" parameterType="com.sfa.job.domain.job.entity.SysJob" resultMap="SysJobResult">
<include refid="selectJobVo"/> <include refid="selectJobVo"/>
<where> <where>
<if test="jobName != null and jobName != ''"> <if test="jobName != null and jobName != ''">
...@@ -63,7 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -63,7 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach> </foreach>
</delete> </delete>
<update id="updateJob" parameterType="com.sfa.job.domain.SysJob"> <update id="updateJob" parameterType="com.sfa.job.domain.job.entity.SysJob">
update sys_job update sys_job
<set> <set>
<if test="jobName != null and jobName != ''">job_name = #{jobName},</if> <if test="jobName != null and jobName != ''">job_name = #{jobName},</if>
...@@ -80,7 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ...@@ -80,7 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where job_id = #{jobId} where job_id = #{jobId}
</update> </update>
<insert id="insertJob" parameterType="com.sfa.job.domain.SysJob" useGeneratedKeys="true" keyProperty="jobId"> <insert id="insertJob" parameterType="com.sfa.job.domain.job.entity.SysJob" useGeneratedKeys="true" keyProperty="jobId">
insert into sys_job( insert into sys_job(
<if test="jobId != null and jobId != 0">job_id,</if> <if test="jobId != null and jobId != 0">job_id,</if>
<if test="jobName != null and jobName != ''">job_name,</if> <if test="jobName != null and jobName != ''">job_name,</if>
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sfa.job.domain.system.mapper.SysDeptMapper">
<resultMap id="BaseResultMap" type="com.sfa.job.domain.system.entity.SysDept">
<id property="deptId" column="dept_id" jdbcType="BIGINT"/>
<result property="deptCode" column="dept_code" jdbcType="VARCHAR"/>
<result property="deptName" column="dept_name" jdbcType="VARCHAR"/>
<result property="fsDeptId" column="fs_dept_id" jdbcType="VARCHAR"/>
<result property="parentId" column="parent_id" jdbcType="BIGINT"/>
<result property="parentCode" column="parent_code" jdbcType="VARCHAR"/>
<result property="parentName" column="parent_name" jdbcType="VARCHAR"/>
<result property="parentFsId" column="parent_fs_id" jdbcType="VARCHAR"/>
<result property="ancestors" column="ancestors" jdbcType="VARCHAR"/>
<result property="ancestorCodes" column="ancestor_codes" jdbcType="VARCHAR"/>
<result property="ancestorNames" column="ancestor_names" jdbcType="VARCHAR"/>
<result property="orderNum" column="order_num" jdbcType="INTEGER"/>
<result property="leader" column="leader" jdbcType="VARCHAR"/>
<result property="fsLeaderId" column="fs_leader_id" jdbcType="VARCHAR"/>
<result property="phone" column="phone" jdbcType="VARCHAR"/>
<result property="email" column="email" jdbcType="VARCHAR"/>
<result property="memberCount" column="member_count" jdbcType="INTEGER"/>
<result property="status" column="status" jdbcType="CHAR"/>
<result property="delFlag" column="del_flag" jdbcType="CHAR"/>
<result property="createBy" column="create_by" jdbcType="VARCHAR"/>
<result property="createUserId" column="create_user_id" jdbcType="BIGINT"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
<result property="updateUserId" column="update_user_id" jdbcType="BIGINT"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<resultMap id="SimpleResultMap" type="com.sfa.job.domain.system.entity.SysDept">
<id property="deptId" column="dept_id" jdbcType="BIGINT"/>
<result property="deptCode" column="dept_code" jdbcType="VARCHAR"/>
<result property="deptName" column="dept_name" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
dept_id
,dept_code,dept_name,
fs_dept_id,parent_id,parent_code,
parent_name,parent_fs_id,ancestors,
ancestor_codes,ancestor_names,order_num,
leader,fs_leader_id,phone,
email,member_count,status,
del_flag,create_by,create_user_id,
create_time,update_by,update_user_id,
update_time
</sql>
<insert id="insertOrUpdate" parameterType="com.sfa.job.domain.system.entity.SysDept">
INSERT INTO sys_dept (dept_code, dept_name, fs_dept_id, parent_id, parent_code, parent_name,
parent_fs_id, ancestors, ancestor_names, order_num,
fs_leader_id, member_count, status, create_by,
create_user_id, update_by, update_user_id)
VALUES (
#{deptCode}, #{deptName}, #{fsDeptId}, #{parentId}, #{parentCode}, #{parentName},
#{parentFsId}, #{ancestors}, #{ancestorNames}, #{orderNum},
#{fsLeaderId}, #{memberCount}, #{status}, #{createBy},
#{createUserId}, #{updateBy}, #{updateUserId}
)
ON DUPLICATE KEY UPDATE dept_name = VALUES(dept_name),
fs_dept_id = VALUES(fs_dept_id),
parent_id = VALUES(parent_id),
parent_code = VALUES(parent_code),
parent_name = VALUES(parent_name),
parent_fs_id = VALUES(parent_fs_id),
ancestors = VALUES(ancestors),
ancestor_names = VALUES(ancestor_names),
order_num = VALUES(order_num),
fs_leader_id = VALUES(fs_leader_id),
member_count = VALUES(member_count),
status = VALUES(status),
update_by = VALUES(update_by),
update_user_id = VALUES(update_user_id);
</insert>
<select id="selectListByStatus" resultMap="SimpleResultMap">
select dept_id,dept_code,dept_name from sys_dept where status = '0' and del_flag = '0';
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sfa.job.domain.system.mapper.SysUserMapper">
<resultMap id="BaseResultMap" type="com.sfa.job.domain.system.entity.SysUser">
<id property="userId" column="user_id" jdbcType="BIGINT"/>
<result property="deptId" column="dept_id" jdbcType="BIGINT"/>
<result property="userName" column="user_name" jdbcType="VARCHAR"/>
<result property="nickName" column="nick_name" jdbcType="VARCHAR"/>
<result property="userType" column="user_type" jdbcType="VARCHAR"/>
<result property="fsUserId" column="fs_user_id" jdbcType="VARCHAR"/>
<result property="email" column="email" jdbcType="VARCHAR"/>
<result property="phonenumber" column="phonenumber" jdbcType="VARCHAR"/>
<result property="sex" column="sex" jdbcType="CHAR"/>
<result property="avatar" column="avatar" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="roleId" column="role_id" jdbcType="BIGINT"/>
<result property="roleName" column="role_name" jdbcType="VARCHAR"/>
<result property="postId" column="post_id" jdbcType="BIGINT"/>
<result property="postName" column="post_name" jdbcType="VARCHAR"/>
<result property="ruleId" column="rule_id" jdbcType="BIGINT"/>
<result property="ruleName" column="rule_name" jdbcType="VARCHAR"/>
<result property="workProvinceNum" column="work_province_num" jdbcType="VARCHAR"/>
<result property="workProvinceName" column="work_province_name" jdbcType="VARCHAR"/>
<result property="workCityNum" column="work_city_num" jdbcType="VARCHAR"/>
<result property="workCityName" column="work_city_name" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="CHAR"/>
<result property="delFlag" column="del_flag" jdbcType="CHAR"/>
<result property="loginIp" column="login_ip" jdbcType="VARCHAR"/>
<result property="loginDate" column="login_date" jdbcType="TIMESTAMP"/>
<result property="createBy" column="create_by" jdbcType="VARCHAR"/>
<result property="createUserId" column="create_user_id" jdbcType="BIGINT"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
<result property="updateUserId" column="update_user_id" jdbcType="BIGINT"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="remark" column="remark" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
user_id,dept_id,user_name,
nick_name,user_type,fs_user_id,
email,phonenumber,sex,
avatar,password,role_id,
role_name,post_id,post_name,
rule_id,rule_name,work_province_num,
work_province_name,work_city_num,work_city_name,
status,del_flag,login_ip,
login_date,create_by,create_user_id,
create_time,update_by,update_user_id,
update_time,remark
</sql>
<insert id="insertOrUpdate" parameterType="com.sfa.job.domain.system.entity.SysUser">
INSERT INTO sys_user (fs_user_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password,
role_id, role_name, work_city_name, status, create_by, create_user_id,fs_open_id)
VALUES (
#{fsUserId},
#{deptId},
#{userName},
#{nickName},
#{userType},
#{email},
#{phonenumber},
#{sex},
#{avatar},
#{password},
#{roleId},
#{roleName},
#{workCityName},
#{status},
#{createBy},
#{createUserId},
#{fsOpenId}
)
ON DUPLICATE KEY UPDATE dept_id = VALUES(dept_id),
nick_name = VALUES(nick_name),
email = VALUES(email),
phonenumber = VALUES(phonenumber),
work_city_name = VALUES(work_city_name),
status = VALUES(status),
update_by = VALUES(update_by),
fs_open_id = VALUES(fs_open_id),
update_user_id = VALUES(update_user_id);
</insert>
<update id="updateDeptLeader">
update sys_dept sd inner join sys_user su
set sd.leader = su.nick_name,
sd.phone = su.phonenumber
where sd.fs_leader_id = su.fs_open_id
</update>
</mapper>
package com.sfa.job.service.system;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* @author : liqiulin
* @date : 2024-12-10 15
* @describe :
*/
public class DeptAndUserServiceImplTest {
@Test
public void userAll() {
}
}
\ No newline at end of file
package com.sfa.job.service.system;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author : liqiulin
* @date : 2024-12-09 13
* @describe :
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class DeptServiceImplTest {
@Autowired
IDeptAndUserService deptService;
@Test
public void deptAll() {
deptService.deptAll();
}
@Test
public void userAll() {
deptService.userAll();
}
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论