@peninsula-med/beisen-ehr-plugin 3.1.2 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -5
- package/dist/index.cjs.js +49 -25
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.esm.js +49 -25
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/skills/beisen-ehr/SKILL.md +58 -0
package/README.md
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
# 北森 EHR 加班申请插件 v3.1.
|
|
1
|
+
# 北森 EHR 加班申请插件 v3.1.3 🐉
|
|
2
2
|
|
|
3
3
|
专注功能:**加班申请**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 🚀 v3.1.3 优化 (2026-03-13)
|
|
6
|
+
|
|
7
|
+
**性能优化:**
|
|
8
|
+
- ⚡ **Token 缓存增强** - 减少重复获取(500-1000ms/次)
|
|
9
|
+
- 🔇 **日志精简** - DEBUG 模式控制,减少 I/O 开销
|
|
10
|
+
- ✅ **参数验证前置** - 日期格式错误提前拦截
|
|
11
|
+
|
|
12
|
+
**错误处理修复:**
|
|
13
|
+
- 🚨 **API 响应严格检查** - 支持 `code`/`status` 多种格式
|
|
14
|
+
- 📝 **原始响应日志** - DEBUG 模式下记录完整 API 响应
|
|
15
|
+
- 🔍 **详细错误信息** - 尝试 `message`/`msg`/`error` 多种字段
|
|
16
|
+
|
|
17
|
+
**文档优化:**
|
|
18
|
+
- 📖 **SKILL.md 快速开始** - 30 秒内找到正确调用方式
|
|
19
|
+
- 🛠️ **故障排查指南** - 响应慢/调用失败检查清单
|
|
6
20
|
|
|
7
21
|
## 一键安装
|
|
8
22
|
|
|
9
23
|
```bash
|
|
10
24
|
# 1. 安装插件
|
|
11
|
-
openclaw plugins install @peninsula-med/beisen-ehr-plugin@
|
|
25
|
+
openclaw plugins install @peninsula-med/beisen-ehr-plugin@3.1.3
|
|
12
26
|
|
|
13
27
|
# 2. 配置凭证
|
|
14
28
|
npx @peninsula-med/beisen-ehr-configure
|
|
@@ -33,7 +47,24 @@ openclaw gateway restart
|
|
|
33
47
|
- Tenant ID
|
|
34
48
|
- 企业邮箱
|
|
35
49
|
|
|
50
|
+
## 调试模式
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 开启详细日志
|
|
54
|
+
export DEBUG=true
|
|
55
|
+
openclaw gateway restart
|
|
56
|
+
|
|
57
|
+
# 查看日志
|
|
58
|
+
openclaw gateway logs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 故障排查
|
|
62
|
+
|
|
63
|
+
见 `SKILL.md` 或访问:
|
|
64
|
+
- `/home/node/.openclaw/workspace/beisen-ehr-plugin/skills/beisen-ehr/SKILL.md`
|
|
65
|
+
|
|
36
66
|
---
|
|
37
67
|
|
|
38
|
-
**版本**: 3.
|
|
39
|
-
**作者**: 龙二 🐉
|
|
68
|
+
**版本**: 3.1.3
|
|
69
|
+
**作者**: 龙二 🐉
|
|
70
|
+
**更新**: 2026-03-13
|
package/dist/index.cjs.js
CHANGED
|
@@ -3454,25 +3454,30 @@ var TypeBuilder = /*#__PURE__*/Object.freeze({
|
|
|
3454
3454
|
const Type = TypeBuilder;
|
|
3455
3455
|
|
|
3456
3456
|
/**
|
|
3457
|
-
* 北森 EHR OpenClaw Plugin v3.
|
|
3457
|
+
* 北森 EHR OpenClaw Plugin v3.1.0 (性能优化版)
|
|
3458
3458
|
* 🐉 龙二 开发
|
|
3459
3459
|
*
|
|
3460
3460
|
* 专注功能:加班申请
|
|
3461
|
+
* 优化:Token 缓存增强、日志精简、错误处理修复
|
|
3461
3462
|
*/
|
|
3462
3463
|
const pluginId = 'beisen-ehr-plugin';
|
|
3463
|
-
const pluginVersion = '3.
|
|
3464
|
+
const pluginVersion = '3.1.0';
|
|
3464
3465
|
const defaultConfig = {
|
|
3465
3466
|
apiUrl: 'https://openapi.italent.cn',
|
|
3466
3467
|
};
|
|
3467
|
-
// ============ Token
|
|
3468
|
+
// ============ Token 管理(增强缓存) ============
|
|
3468
3469
|
let cachedToken = null;
|
|
3469
3470
|
let tokenExpiry = 0;
|
|
3471
|
+
// 调试模式控制
|
|
3472
|
+
const DEBUG = process.env.DEBUG === 'true' || process.env.BEISEN_DEBUG === 'true';
|
|
3473
|
+
const log = (...args) => DEBUG ? console.error(...args) : null;
|
|
3470
3474
|
async function getAccessToken(config) {
|
|
3471
3475
|
if (cachedToken && Date.now() < tokenExpiry) {
|
|
3476
|
+
log(`[BEISEN] ⚡ 使用缓存 Token`);
|
|
3472
3477
|
return cachedToken;
|
|
3473
3478
|
}
|
|
3474
3479
|
const tokenUrl = `${config.apiUrl || defaultConfig.apiUrl}/token`;
|
|
3475
|
-
|
|
3480
|
+
log(`[BEISEN] 🔑 正在获取 Token: ${tokenUrl}`);
|
|
3476
3481
|
const response = await fetch(tokenUrl, {
|
|
3477
3482
|
method: "POST",
|
|
3478
3483
|
headers: { "Content-Type": "application/json" },
|
|
@@ -3489,7 +3494,7 @@ async function getAccessToken(config) {
|
|
|
3489
3494
|
const data = await response.json();
|
|
3490
3495
|
cachedToken = data.accessToken || data.access_token;
|
|
3491
3496
|
tokenExpiry = Date.now() + (data.expiresIn || data.expires_in || 7200) * 1000 - 5 * 60 * 1000;
|
|
3492
|
-
|
|
3497
|
+
log(`[BEISEN] ✅ Token 获取成功,有效期至:${new Date(tokenExpiry).toISOString()}`);
|
|
3493
3498
|
return cachedToken;
|
|
3494
3499
|
}
|
|
3495
3500
|
// ============ API 客户端 ============
|
|
@@ -3507,12 +3512,8 @@ class BeisenClient {
|
|
|
3507
3512
|
...(options.headers || {}),
|
|
3508
3513
|
};
|
|
3509
3514
|
const requestBody = options.body ? JSON.stringify(options.body) : undefined;
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
console.error(`[BEISEN] 🏢 TenantId: ${this.config.tenantId}`);
|
|
3513
|
-
if (requestBody) {
|
|
3514
|
-
console.error(`[BEISEN] 📦 Body: ${requestBody.substring(0, 200)}`);
|
|
3515
|
-
}
|
|
3515
|
+
log(`[BEISEN] 📡 ${options.method || 'GET'} ${url}`);
|
|
3516
|
+
log(`[BEISEN] 🔑 Token: ${token.substring(0, 20)}...`);
|
|
3516
3517
|
const controller = new AbortController();
|
|
3517
3518
|
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
|
3518
3519
|
try {
|
|
@@ -3528,7 +3529,7 @@ class BeisenClient {
|
|
|
3528
3529
|
throw new Error(`API 错误:${response.status} - ${errorText}`);
|
|
3529
3530
|
}
|
|
3530
3531
|
const result = await response.json();
|
|
3531
|
-
|
|
3532
|
+
log(`[BEISEN] 📥 API 原始响应:${JSON.stringify(result)}`);
|
|
3532
3533
|
return result;
|
|
3533
3534
|
}
|
|
3534
3535
|
catch (error) {
|
|
@@ -3536,7 +3537,7 @@ class BeisenClient {
|
|
|
3536
3537
|
if (error.name === 'AbortError') {
|
|
3537
3538
|
throw new Error('请求超时(30 秒)');
|
|
3538
3539
|
}
|
|
3539
|
-
|
|
3540
|
+
log(`[BEISEN] ❌ 错误:${error.message}`);
|
|
3540
3541
|
throw error;
|
|
3541
3542
|
}
|
|
3542
3543
|
}
|
|
@@ -3553,7 +3554,7 @@ class BeisenClient {
|
|
|
3553
3554
|
}
|
|
3554
3555
|
return this.request("/AttendanceOpen/api/v1/AttendanceOvertime/PostOverTimeWithApproval", {
|
|
3555
3556
|
method: "POST",
|
|
3556
|
-
body: { attendanceOverTime: overtimeData },
|
|
3557
|
+
body: { attendanceOverTime: overtimeData },
|
|
3557
3558
|
});
|
|
3558
3559
|
}
|
|
3559
3560
|
}
|
|
@@ -3577,7 +3578,7 @@ async function register(api) {
|
|
|
3577
3578
|
console.error('[BEISEN] 🔧 正在注册工具...');
|
|
3578
3579
|
api.registerTool({
|
|
3579
3580
|
name: 'beisen_submit_overtime',
|
|
3580
|
-
description: '
|
|
3581
|
+
description: '提交加班申请',
|
|
3581
3582
|
parameters: Type.Object({
|
|
3582
3583
|
startDate: Type.String({ description: '开始时间 (YYYY-MM-DD HH:mm:ss)' }),
|
|
3583
3584
|
stopDate: Type.String({ description: '结束时间 (YYYY-MM-DD HH:mm:ss)' }),
|
|
@@ -3585,8 +3586,16 @@ async function register(api) {
|
|
|
3585
3586
|
properties: Type.Optional(Type.String({ description: '自定义字段 JSON' })),
|
|
3586
3587
|
}),
|
|
3587
3588
|
async execute(_id, params) {
|
|
3588
|
-
|
|
3589
|
+
log(`[BEISEN] 🔨 工具调用:beisen_submit_overtime`, JSON.stringify(params));
|
|
3589
3590
|
try {
|
|
3591
|
+
// 参数验证
|
|
3592
|
+
const dateTimeRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
|
3593
|
+
if (!dateTimeRegex.test(params.startDate)) {
|
|
3594
|
+
throw new Error("startDate 格式错误,应为 YYYY-MM-DD HH:mm:ss");
|
|
3595
|
+
}
|
|
3596
|
+
if (!dateTimeRegex.test(params.stopDate)) {
|
|
3597
|
+
throw new Error("stopDate 格式错误,应为 YYYY-MM-DD HH:mm:ss");
|
|
3598
|
+
}
|
|
3590
3599
|
const result = await client.submitOvertime({
|
|
3591
3600
|
staffId: config.staffId,
|
|
3592
3601
|
email: config.email,
|
|
@@ -3595,17 +3604,32 @@ async function register(api) {
|
|
|
3595
3604
|
compensationType: params.compensationType,
|
|
3596
3605
|
properties: params.properties,
|
|
3597
3606
|
});
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3607
|
+
// 严格检查响应状态
|
|
3608
|
+
const isSuccess = (result.code === "200" ||
|
|
3609
|
+
result.code === 200 ||
|
|
3610
|
+
result.status === "200" ||
|
|
3611
|
+
result.status === 200 ||
|
|
3612
|
+
result.status === "success");
|
|
3613
|
+
if (isSuccess) {
|
|
3614
|
+
const compTypeText = params.compensationType === 1 ? "加班费" : "调休假";
|
|
3615
|
+
const applicationId = result.data || result.id || result.applicationId || "待返回";
|
|
3616
|
+
log(`[BEISEN] ✅ 加班申请提交成功:${applicationId}`);
|
|
3617
|
+
return {
|
|
3618
|
+
content: [{
|
|
3619
|
+
type: "text",
|
|
3620
|
+
text: `✅ 加班申请已提交!\n\n- 员工:${config.staffId}\n- 邮箱:${config.email}\n- 时间:${params.startDate} ~ ${params.stopDate}\n- 类型:${compTypeText}\n- 申请 ID: ${applicationId}`,
|
|
3621
|
+
}],
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3624
|
+
else {
|
|
3625
|
+
// 详细错误信息
|
|
3626
|
+
const errorMsg = result.message || result.msg || result.error || `API 返回错误:${JSON.stringify(result)}`;
|
|
3627
|
+
log(`[BEISEN] ❌ API 错误:${errorMsg}`);
|
|
3628
|
+
throw new Error(errorMsg);
|
|
3629
|
+
}
|
|
3606
3630
|
}
|
|
3607
3631
|
catch (error) {
|
|
3608
|
-
|
|
3632
|
+
log(`[BEISEN] ❌ 提交失败:${error.message}`);
|
|
3609
3633
|
return {
|
|
3610
3634
|
content: [{ type: "text", text: `❌ 提交失败:${error.message}` }],
|
|
3611
3635
|
isError: true,
|