在泛微 E9 系统中读取组织架构及所有员工信息

缘起公司因为受够了无端的弱密码爆破而断开了所有服务对外的 NAT/端口映射,所有的服务都只能通过 VPN 访问了。为了降低管理上的时间成本,最近自己给自己找了个需求:将公司 ecology-9 OA 内登记的所有员工配置同步到新购置的 atrust 零信任设备,以便新员工入职自动开启 atrust VPN 权限,员工离职后也能及时收回内网的访问权限。

我们公司是一个对微软域套件,甚至微软操作系统都依赖不重的公司,对 AD 域、LDAP 等技术感冒不了一点,现在没有在使用,以后也断然不会使用。索性同步用户这种事就利用这些内网服务的 Open API 配合消息队列+微服务去完成吧。

本文章是“先进摸鱼学”系列文章中的第一篇,实际上我暂时没有确定具体的实践架构如何,“消息队列+微服务“甚至是”数据中台“这种更规范的模式或许不能因地制宜。根据我现在对系统结构和功能的理解,或许最合适的方案是:
反向同步端(OA)【轮询OA用户列表 ->全量读取 -> 数据清洗(仅保留在职真实员工) -> 数据库落盘】
正向同步端(atrust)【轮询设备端用户列表 -> 全量读取 -> 与数据库比对 -> 配置设备端用户列表及权限】


1、开通人力资源 RESTful API 访问权限

有两种方式可以获取访问权限,一种是平时开发最常见的 token+appid,另一种是 IP 地址白名单。我甚至觉得官方是有意避免用户使用 token+appid 进行开发的。因为在 ecology 中居然没有提供现有的配置业务来实现该功能,该功能完全是通过在数据库中执行 SQL 来完成的。对于生产环境来说,这种对业务不透明的修改是不能接受的。

所以我们使用最简单,也是最安全的 IP 白名单方式来实现授权:
编辑两个 HrmService 人力资源模块的配置文件,加入你的用户同步业务服务器和开发机的 IP 地址

# 文件一
\WEAVER\ecology\WEB-INF\prop\HrmOutInterfaceIP.properties
#isopen IP范围限制是否开启,1:开启 0:关闭
#调用人力资源外部接口的IP范围,多个中间请用逗号隔开 
isopen=1
ipaddress=127.0.0.1,192.168.4.129,192.168.3.19

# 文件二
WEAVER\ecology\WEB-INF\prop\HrmWebserviceIP.properties
#调用人力资源webservice接口的ip范围,多个中间请用逗号隔开
ipaddress=127.0.0.1

无需重启 ecology 中断业务,现在服务器已经可以接受来自用户同步业务服务器和开发机的 API 访问。

2、企业及员工信息架构

逻辑上,OA 的员工是以这种结构进行组织的:

分部(subcompany)下设部门
部门中含有 subcompanyid1 字段关联到对应的分部
如果部门有自己的父部门,则通过 supdepid 字段进行自引用(如果为顶级部门则 supdepid 值为 0)
部门下有员工,使用 departmentid 字段关联到对应的部门,也使用 jobtitle 字段关联到员工自己的岗位

综合所有的信息,可以为每个员工得到这样的路径:
/SubcompanyName/DepartmentA/SubDepartmentB/UserName

例子:
/北京分部/技术部/前端组/张三

3、读取企业及员工信息

具体接口说明可参考泛微 E9 产品官方文档
https://e-cloudstore.com/doc.html?appId=c373a4b01fb74d098b62e2b969081d2d

实现这些接口的分页获取后,就可以对所有的信息进行链接整合了。我使用 node.js 进行开发,这里使用 Map 创建元素之间的映射,取数据时直接获取每对映射关系即可:

// /src/userInfoProcessor.js

const { getDepartments, getJobtitles, getSubcompanies, getUsers } = require('./api');
const { fetchAllSerial } = require('./utils/paginateFetch');

module.exports = async function processUserInfo() {
  try {
    const subcompanies = await fetchAllSerial(getSubcompanies);
    const departments = await fetchAllSerial(getDepartments);
    const jobtitles = await fetchAllSerial(getJobtitles);
    const users = await fetchAllSerial(getUsers);

    // 分部 (id、分部名称)
    const subcompanyMap = Object.fromEntries(subcompanies.map(s => [s.id, s.subcompanyname]));
    // 部门 (id、部门名称、所属上级部门id(为0时说明该部门为顶级部门))
    const departmentMap = Object.fromEntries(departments.map(d => [d.id, { name: d.departmentname, supdepid: d.supdepid }]));
    // 岗位 (id、岗位名称)
    const jobMap = Object.fromEntries(jobtitles.map(j => [j.id, j.jobtitlename]));
    // 用户 (id、姓名)
    const userMap = Object.fromEntries(users.map(u => [u.id, u.lastname]));

    const usersInfo = users.map(user => {
      const departmentsArray = [];
      let currentDepId = user.departmentid;

      while (currentDepId !== undefined && currentDepId !== null && currentDepId !== 0) {
        const currentDep = departmentMap[currentDepId];
        if (currentDep) {
          departmentsArray.unshift(currentDep.name);
          currentDepId = currentDep.supdepid;
        } else {
          break;
        }
      }

      const subcompanyName = subcompanyMap[user.subcompanyid1] || '未知分部';
      departmentsArray.unshift(subcompanyName);

      return {
        id: user.id,
        loginid: user.loginid,
        name: user.lastname,
        jobtitle: jobMap[user.jobtitle] || '',
        manager: userMap[user.managerid] || '',
        department: departmentsArray.length > 0 ? departmentsArray : ['']
      };
    });

    return usersInfo;
  } catch (error) {
    console.error('处理用户信息时出错:', error);
    throw error;
  }
}

调用该模块时,可以得到这样的输出:

// index.js

const processUserInfo = require('./src/userInfoProcessor');

(async () => {
  try {
    const usersInfo = await processUserInfo();
    console.table(usersInfo);
  } catch (error) {
    console.error('执行过程中出错:', error);
  }
})();

获取到的用户有许多字段,取出自己感兴趣的字段将其落盘存储即可。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇