591 lines
27 KiB
Markdown
591 lines
27 KiB
Markdown
# 考勤排班操作参考 (attendance-schedule)
|
||
|
||
> 本文档由 `attendance.md` 路由调用。覆盖两类排班操作:
|
||
> 1. **排班导入**(写操作):当用户提到"排班"、"导入排班"、"安排班次"、"设置排班"、"调班"、"换班"、"排休"时
|
||
> 2. **排班查询导出**(只读操作):当用户提到"查看排班"、"排班表"、"导出排班"、"排班记录"时
|
||
>
|
||
> 不适用于:班次定义查询(用 `attendance class search`)、考勤组配置(用 `attendance group get`)。
|
||
|
||
## 强制门禁(必须先读完本文档才能执行)
|
||
|
||
**任何排班操作都必须经过本文档定义的工作流,严禁绕过本文档直接调用 `dws attendance schedule import` 命令。**
|
||
|
||
违反将出现以下任一问题:
|
||
1. 未按"阶段 1"确认考勤组 → 把固定班制考勤组当排班制操作,接口报错
|
||
2. 未按"阶段 3"校验班次 → 传入不属于该考勤组的班次 ID,导致排班数据错乱
|
||
3. 未按"阶段 4"回显确认 → 用户未看到排班内容就直接执行,排错了无法回退
|
||
4. 未按"阶段 2"解析人员 → 传入错误的 userId,导致排班到错误的人
|
||
5. 未经用户确认就执行排班 → 排班是写操作,一旦执行就会覆盖原有排班
|
||
|
||
**执行前自检(必须能在心中回答)**:
|
||
- [ ] 考勤组是排班制(TURN)吗?
|
||
- [ ] 员工都属于该考勤组吗?
|
||
- [ ] 班次都属于该考勤组可用的班次吗?
|
||
- [ ] 用户已经确认了排班内容吗?
|
||
|
||
如果上述任何一项答不出,**回到本文档对应章节重新阅读**,禁止凭记忆/想象组装命令。
|
||
|
||
**前提**:当前用户必须是钉钉考勤管理员,否则排班接口返回权限错误。
|
||
|
||
## 业务约束(必须深刻理解)
|
||
|
||
> **这两条约束是排班的根基,贯穿整个工作流的每一步。**
|
||
|
||
1. **用户只能属于一个考勤组**:每个员工有且只有一个考勤组,不存在"选择考勤组"的场景。直接通过 `dws attendance rules` 查询即可唯一确定。
|
||
2. **排班只能排考勤组关联的班次**:考勤组绑定了固定的班次列表(`shiftVOList`),排班时只能从这些班次中选择,不能使用企业其他考勤组的班次,更不能编造班次。
|
||
|
||
**由此推导出的执行顺序**:必须先查清考勤组和它关联的班次,再去收集日期、人员等其他参数。
|
||
|
||
## 核心原则
|
||
|
||
Agent 解析用户意图(考勤组、员工、日期范围、班次安排),完成校验后,**调用 Python 脚本执行排班**。
|
||
- **先查后排**:任何排班操作的第一步都是查询考勤组及其关联班次,拿到真实数据后再进行后续参数收集
|
||
- **脚本自包含**:考勤组校验、班次校验、员工校验、回显确认、调用排班 API 全部由脚本内部完成
|
||
- **Agent 职责**:先查考勤组和关联班次,再解析用户意图、获取必要的 ID(员工 userId)、组装脚本参数
|
||
- **脚本职责**:二次校验、回显排班表格、等待用户确认、执行排班、输出结果摘要
|
||
- 排班是**写操作**,必须经过回显确认后才能执行
|
||
|
||
## 严格禁止 (NEVER DO)
|
||
|
||
- **禁止直接调用 `dws attendance schedule import`**,必须通过脚本执行
|
||
- 禁止凭历史记忆复用任何 ID(考勤组 ID、班次 ID、userId),必须从当次命令返回值中提取
|
||
- 禁止在未确认考勤组类型为排班制(TURN)的情况下执行排班
|
||
- 禁止在班次未经校验的情况下执行排班
|
||
- 禁止跳过用户确认直接执行排班
|
||
- **禁止在未向用户展示完整排班明细表格(含每天的班次名称)的情况下弹出确认卡片**。用户必须先看到"谁、哪天、上什么班"才能做出确认决策
|
||
- 禁止编造任何字段值或用户姓名
|
||
- 禁止直接输出裸 userId,脚本已内置 userId → 姓名转换
|
||
- **禁止直接输出裸 classId 数字**,必须展示班次名称(如"早班"),用户不理解 classId 是什么
|
||
|
||
## 严格要求 (MUST DO)
|
||
|
||
- 所有 `dws` 命令必须携带 `--format json`
|
||
- 必须先确认考勤组类型为 TURN(排班制),否则拒绝执行
|
||
- 必须通过考勤组详情获取绑定班次列表,校验班次 ID 属于该考勤组
|
||
- 必须在执行排班前向用户回显排班内容并获得确认
|
||
- 任何接口失败必须向用户清晰报错,禁止静默吞掉
|
||
|
||
## 涉及工具
|
||
|
||
| 工具 | 用途 | 安全等级 |
|
||
|------|------|---------|
|
||
| `dws attendance group search` | 搜索考勤组(按名称/类型) | 只读 |
|
||
| `dws attendance group get` | 查询考勤组全量信息(含绑定班次列表) | 只读 |
|
||
| `dws attendance group filtered-get` | 查询考勤组详情(成员列表) | 只读 |
|
||
| `dws attendance class search` | 查询班次列表(ID→名称映射) | 只读 |
|
||
| `dws attendance class get` | 查询班次详情 | 只读 |
|
||
| `dws attendance schedule import` | 导入排班记录(**仅由脚本内部调用**) | 写操作(危险) |
|
||
| `dws attendance schedule get` | 查询现有排班记录 | 只读 |
|
||
| `dws aisearch person` | 按姓名搜索用户获取 userId | 只读 |
|
||
| `dws contact user get` | 批量查询 userId → 用户信息 | 只读 |
|
||
| `dws contact dept search` | 搜索部门获取 deptId | 只读 |
|
||
| `dws contact dept list-members` | 获取部门成员 userId 列表 | 只读 |
|
||
|
||
## 意图判断
|
||
|
||
### 排班操作类型
|
||
|
||
| 用户说 | 操作类型 | 处理方式 |
|
||
|--------|---------|---------|
|
||
| "帮我给研发组排下周的班" / "排班" / "安排班次" | 批量排班 | 走本文档工作流 |
|
||
| "帮我把张三下周一改成早班" / "调班" / "换班" | 单人调班 | 走本文档工作流(单人模式) |
|
||
| "帮我把李四下周三排休" | 排休 | 走本文档工作流(isRest=Y) |
|
||
|
||
### 易混淆场景
|
||
|
||
| 用户说 | 应路由到 |
|
||
|--------|---------|
|
||
| "查看下周的排班" / "排班表" / "导出排班" / "导出排班表" / "XX考勤组的排班" | **本文档「排班查询导出工作流」**(走脚本) |
|
||
| "有哪些班次" / "班次列表" | `dws attendance class search`(查询班次定义) |
|
||
| "我属于哪个考勤组" | `dws attendance rules`(查询考勤规则) |
|
||
| "导出考勤报表" / "导出考勤" / "考勤明细" / "出勤汇总" (**不含"排班"二字**) | `attendance-report.md`(报表 skill) |
|
||
|
||
> **关键区分**:"导出排班表" ≠ "导出考勤报表"。判断标准:句中含"排班"→ 本文档;不含"排班"且说的是"考勤报表/考勤数据/出勤统计" → `attendance-report.md`。
|
||
|
||
## 工作流
|
||
|
||
### 阶段 0: 先查考勤组和关联班次,再收集缺失参数
|
||
|
||
> **核心逻辑:先查后问。** 用户只属于一个考勤组,排班只能排该考勤组关联的班次。所以第一步永远是查清考勤组和它的班次,拿到真实数据后再向用户收集其他信息。
|
||
|
||
**步骤 0a — 查询考勤组(必须最先执行)**:
|
||
|
||
```bash
|
||
# 自动获取当前用户的考勤组(用户只属于一个考勤组,无需选择)
|
||
dws attendance rules --date <今天日期> --format json
|
||
# → 从返回中提取 groupId
|
||
```
|
||
|
||
如果是给指定员工排班,先查该员工的 userId,再查其考勤组。
|
||
|
||
**步骤 0b — 查询考勤组详情和关联班次(必须在 ask_question 之前完成)**:
|
||
|
||
```bash
|
||
# 获取考勤组详情(含绑定的班次列表)
|
||
dws attendance group get --group-id <groupId> --format json
|
||
# → 校验 groupVO.type 必须为 TURN(排班制)
|
||
# → 从 groupVO.shiftVOList 提取关联的班次
|
||
```
|
||
|
||
**提取结果**:
|
||
- 考勤组名称:`groupVO.name`
|
||
- 考勤组类型:`groupVO.type`(必须为 TURN)
|
||
- 关联班次列表:`groupVO.shiftVOList[].shiftSetting.{shiftId, shiftName}`
|
||
|
||
**步骤 0c — 收集缺失参数(ask_question 卡片交互)**:
|
||
|
||
拿到考勤组和关联班次后,再向用户收集缺失的参数。排班所需的四个参数:
|
||
- **考勤组**:已在 0a 自动获取,无需询问
|
||
- **班次**:已在 0b 获取关联班次列表,展示给用户选择
|
||
- **员工范围**(必填):指定员工姓名 / 部门 / 考勤组全员 / "给我排班"
|
||
- **日期范围**(必填):具体日期 / 日期范围(如"下周"、"5月19日到5月23日")
|
||
|
||
**只收集真正缺失的参数**,用户已经提供的不要重复询问。将缺失参数**合并到一次 `ask_question` 调用中**。
|
||
|
||
示例:用户说"帮我排班",Agent 应先自动查询考勤组和关联班次(步骤 0a + 0b),然后只询问日期范围和班次:
|
||
|
||
```
|
||
// ===== 步骤 0a + 0b 已完成,此时你已经拿到了以下真实数据 =====
|
||
// groupId = 实际的考勤组ID
|
||
// groupName = 实际的考勤组名称
|
||
// shiftVOList = 考勤组关联的班次列表(来自 dws attendance group get 的返回)
|
||
|
||
// 从 shiftVOList 构建班次选项(伪代码):
|
||
shiftOptions = []
|
||
for each shift in groupVO.shiftVOList:
|
||
shiftOptions.append({ id: String(shift.shiftSetting.shiftId), label: shift.shiftSetting.shiftName })
|
||
shiftOptions.append({ id: "rest", label: "排休" })
|
||
|
||
// 如果考勤组只关联了一个班次,直接使用,不需要询问用户
|
||
if shiftVOList.length == 1:
|
||
selectedShift = shiftVOList[0] // 自动选定,跳过班次选择
|
||
|
||
// 构建日期选项(必须填入实际计算的日期):
|
||
todayStr = 当天日期(YYYY-MM-DD)
|
||
thisWeekEnd = 本周日日期
|
||
nextWeekStart = 下周一日期
|
||
nextWeekEnd = 下周日日期
|
||
|
||
ask_question({
|
||
title: "排班参数确认",
|
||
questions: [
|
||
{
|
||
id: "date_range",
|
||
prompt: "请选择排班日期范围",
|
||
options: [
|
||
{ id: "this_week", label: "本周剩余时间(" + todayStr + "-" + thisWeekEnd + ")" },
|
||
{ id: "next_week", label: "下周(" + nextWeekStart + "-" + nextWeekEnd + ")" },
|
||
{ id: "custom", label: "自定义日期范围" }
|
||
]
|
||
},
|
||
{
|
||
id: "shift",
|
||
prompt: "请选择班次(以下为您考勤组「" + groupName + "」关联的班次)",
|
||
options: shiftOptions // 直接使用上面从 shiftVOList 动态构建的选项,严禁替换为任何硬编码值
|
||
}
|
||
]
|
||
})
|
||
```
|
||
|
||
**⚠️ 班次选项严禁硬编码**:上述伪代码中的 `shiftOptions` 必须在运行时从 `shiftVOList` 动态生成。禁止在 `ask_question` 中写入任何固定的班次名称(如"早班"、"晚班"、"正常班"、"全部排XX班"等)。如果你发现自己在 options 里手写班次名,说明你做错了。
|
||
|
||
**动态选项原则(严格执行)**:
|
||
- 班次选项**必须且只能**来自考勤组的 `shiftVOList`,**严禁编造任何班次名称**(如"正常班"、"早班"、"晚班"、"全部排XX班"等都是编造)
|
||
- 每个班次选项的 `id` 必须是 `shiftVOList` 中的真实 `shiftId`,`label` 必须是真实的 `shiftName`
|
||
- 如果 `shiftVOList` 为空,降级从 `groupVO.classIds` + `class search` 按 ID 精确查询(不是全局搜索)
|
||
- **只收集缺失的参数**:用户已经提供的参数不要重复询问
|
||
- **如果考勤组只有一个班次,直接使用该班次,不需要询问用户选择**
|
||
- **禁止用 `class search` 全局搜索来给用户展示班次选项**——全局班次列表包含不属于该考勤组的班次,用户选了也会被校验拒绝
|
||
|
||
### 阶段 1: 确认考勤组(必须为排班制)
|
||
|
||
> **业务事实:用户只属于一个考勤组。** 不存在"选考勤组"的场景,直接通过 `dws attendance rules` 自动获取即可。只有在用户明确指定了一个考勤组名称时,才用 `group search` 按名称确认。
|
||
|
||
**方式 A — 自动获取(默认方式,适用于绝大多数场景)**:
|
||
```bash
|
||
# 用户只属于一个考勤组,直接查询即可确定
|
||
dws attendance rules --date <今天日期> --format json
|
||
# → 返回中提取 groupId,然后用 group get 获取详情
|
||
dws attendance group get --group-id <groupId> --format json
|
||
```
|
||
|
||
**方式 B — 用户明确指定考勤组名称时**:
|
||
```bash
|
||
dws attendance group search --query "<考勤组名称>" --type TURN --format json
|
||
```
|
||
|
||
**校验规则**:
|
||
1. 考勤组类型必须为 **TURN(排班制)**,如果是 FIXED(固定班制)或 NONE(自由工时),拒绝并提示"该考勤组不是排班制,无法进行排班操作"
|
||
2. 确认考勤组后,**必须立即获取其关联班次**(`groupVO.shiftVOList`),后续所有班次选项都从这里取
|
||
|
||
**提取信息**:考勤组 ID(`groupId`)、考勤组名称(`name`)、**关联班次列表(`shiftVOList`)**
|
||
|
||
### 阶段 2: 获取员工列表
|
||
|
||
**场景 A — 指定员工姓名**:
|
||
```bash
|
||
dws aisearch person --query "<员工姓名>" --dimension name --format json
|
||
```
|
||
|
||
**场景 B — 按部门查询**:
|
||
```bash
|
||
dws contact dept search --query "<部门名>" --format json
|
||
dws contact dept list-members --ids <deptId> --format json
|
||
```
|
||
|
||
**场景 C — 考勤组全员**:
|
||
```bash
|
||
dws attendance group filtered-get --group-id <groupId> --member --format json
|
||
```
|
||
|
||
**场景 D — 用户已给 userId 列表**:直接跳过本步。
|
||
|
||
### 阶段 3: 校验班次(必须是考勤组关联的班次)
|
||
|
||
> **业务约束:排班只能排考勤组关联的班次。** 阶段 0/1 已经通过 `dws attendance group get` 拿到了 `shiftVOList`,本阶段直接使用该数据校验,**不需要也不应该再调用 `class search` 全局搜索**。
|
||
|
||
**班次数据来源**(已在阶段 0 或阶段 1 获取):
|
||
- `groupVO.shiftVOList[].shiftSetting.shiftId` — 班次 ID
|
||
- `groupVO.shiftVOList[].shiftSetting.shiftName` — 班次名称
|
||
|
||
**禁止调用 `dws attendance class search` 全局搜索班次**——全局搜索会返回不属于该考勤组的班次,即使用户指定了班次名称,也必须在 `shiftVOList` 中匹配,而不是全局搜索。
|
||
|
||
**校验规则**:
|
||
1. 用户在 `ask_question` 卡片中选择的班次,其 `shiftId` 必须存在于 `shiftVOList` 中(卡片选项本身就是从 `shiftVOList` 构建的,所以天然满足)
|
||
2. 如果用户通过自然语言指定了班次名称(如"排早班"),必须在 `shiftVOList` 中**按名称模糊匹配**,找到对应的 `shiftId`
|
||
3. 如果用户指定的班次不在 `shiftVOList` 中,**必须拒绝**,并列出该考勤组关联的全部班次让用户重新选择
|
||
4. 仅当 `shiftVOList` 为空时,才降级从 `groupVO.classIds` + `dws attendance class get` 按 ID 精确查询(仍然不是全局搜索)
|
||
|
||
**提取信息**:班次 ID(`classId` / `shiftId`)、班次名称(`shiftName`)
|
||
|
||
### 阶段 4: 回显排班内容并使用 ask_question 卡片确认
|
||
|
||
> **[硬性门禁]** 必须先用普通文本向用户展示**完整的排班明细表格**(包含每个人、每天、具体班次名称),用户看到排班明细后,才能弹出 `ask_question` 确认卡片。
|
||
> **禁止在用户还不知道"谁、哪天、上什么班"的情况下就弹确认卡片**——这等于让用户盲签,体验极差。
|
||
|
||
**步骤 4a — 展示排班明细(必须在确认卡片之前)**:
|
||
|
||
在调用 `ask_question` 之前,**必须先**用普通文本向用户展示排班内容。表格中**必须包含班次名称**(如"早班 09:00-18:00"),不能只展示 classId 数字:
|
||
|
||
```
|
||
排班预览
|
||
|
||
考勤组: <考勤组名称>(ID: <groupId>)
|
||
排班日期: <startDate> ~ <endDate>
|
||
|
||
| 员工姓名 | 日期 | 星期 | 班次 | 是否排休 |
|
||
|---------|------|------|------|---------|
|
||
| 张三 | 2026-05-19 | 周一 | 早班 09:00-18:00 | 否 |
|
||
| 张三 | 2026-05-20 | 周二 | 早班 09:00-18:00 | 否 |
|
||
| 张三 | 2026-05-21 | 周三 | 排休 | 是 |
|
||
| 李四 | 2026-05-19 | 周一 | 晚班 18:00-02:00 | 否 |
|
||
| ... | ... | ... | ... | ... |
|
||
|
||
共 <N> 条排班记录
|
||
```
|
||
|
||
**自查清单(展示表格前必须确认)**:
|
||
- [ ] 表格中有员工姓名(不是 userId)
|
||
- [ ] 表格中有具体日期和星期几
|
||
- [ ] 表格中有班次名称(不是 classId 数字)
|
||
- [ ] 排休的记录标注了"排休"
|
||
- [ ] 表格涵盖了所有待排班的员工和日期
|
||
|
||
**步骤 4b — 使用 `ask_question` 卡片确认(必须在展示表格之后)**:
|
||
|
||
```
|
||
ask_question({
|
||
title: "排班执行确认",
|
||
questions: [
|
||
{
|
||
id: "confirm_execute",
|
||
prompt: "以上排班将覆盖所选日期的现有排班记录,确认执行吗?",
|
||
options: [
|
||
{ id: "yes", label: "确认执行" },
|
||
{ id: "no", label: "取消" }
|
||
]
|
||
}
|
||
]
|
||
})
|
||
```
|
||
|
||
**处理用户选择**:
|
||
- 用户选择 **"确认执行"** → 进入阶段 5
|
||
- 用户选择 **"取消"** → 终止流程,提示"已取消排班操作"
|
||
|
||
### 阶段 5: 调用脚本执行排班
|
||
|
||
```bash
|
||
python scripts/attendance_schedule_import.py \
|
||
--group-id <groupId> \
|
||
--schedules '<JSON数组>' \
|
||
--confirm
|
||
```
|
||
|
||
参数说明:
|
||
- `--group-id`(必填):考勤组 ID
|
||
- `--schedules`(必填):排班记录 JSON 数组,每条记录包含 `userId`、`workDate`、`classId`、`isRest`
|
||
- `--confirm`(必填):表示用户已确认,脚本收到此标志才会执行排班
|
||
|
||
脚本内部自动处理:
|
||
1. 二次校验考勤组类型(必须为 TURN)
|
||
2. 从考勤组详情提取绑定班次,二次校验班次 ID 属于该考勤组
|
||
3. 格式化 workDate 为 `yyyy-MM-dd HH:mm:ss`
|
||
4. 调用 `dws attendance schedule import` 执行排班
|
||
5. 输出执行结果摘要(含全部排班明细)
|
||
|
||
### 阶段 6: 返回结果给用户
|
||
|
||
- 将脚本 stdout 输出的摘要信息原样转告用户
|
||
- 如果脚本输出 warning,原样转告用户
|
||
- 如果执行失败,将 stderr 错误信息转告用户
|
||
|
||
## 排班记录 JSON 格式
|
||
|
||
每条排班记录的字段说明:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `userId` | string | 是 | 员工的 userId |
|
||
| `workDate` | string | 是 | 排班日期,格式 YYYY-MM-DD |
|
||
| `classId` | int | 是 | 班次 ID(从 `class search` 获取) |
|
||
| `isRest` | string | 是 | 是否排休,`Y`=排休 / `N`=正常上班 |
|
||
|
||
排休时 `classId` 传 0,`isRest` 传 `Y`。
|
||
|
||
## API 返回结构注意事项(Agent 必读)
|
||
|
||
> 以下是实际执行中多次踩坑的关键数据结构说明。**禁止凭直觉假设字段在顶层**,必须按本节描述的嵌套路径提取。
|
||
|
||
### `dws attendance group get` 返回结构
|
||
|
||
```
|
||
run_dws 解包后的结构(unwrap_result 去掉 success/result 包装后):
|
||
{
|
||
"groupVO": { ← 关键!type/name/classIds 等字段在这一层
|
||
"type": "TURN", ← 考勤组类型
|
||
"name": "研发组",
|
||
"classIds": [1290384739, ...], ← 绑定的班次 ID 列表
|
||
"shiftVOList": [ ← 排班制特有,班次详情
|
||
{
|
||
"shiftSetting": {
|
||
"shiftId": 1290384739, ← 班次 ID(与 classIds 对应)
|
||
"shiftName": "早班 09:00-18:00"
|
||
}
|
||
}
|
||
],
|
||
"selectedClass": [...], ← 部分环境使用此字段
|
||
...
|
||
},
|
||
...其他顶层字段...
|
||
}
|
||
```
|
||
|
||
**提取规则**:
|
||
- 考勤组类型:`result["groupVO"]["type"]`
|
||
- 考勤组名称:`result["groupVO"]["name"]`
|
||
- 绑定班次 ID 列表:`result["groupVO"]["classIds"]`
|
||
- 班次名称:`result["groupVO"]["shiftVOList"][N]["shiftSetting"]["shiftName"]`
|
||
- **禁止从 result 顶层直接取 type/name/classIds,那里没有这些字段**
|
||
|
||
### `dws attendance class search` 返回结构
|
||
|
||
```
|
||
run_dws 解包后可能为以下之一:
|
||
1. 直接 list[dict]: [{id, name, ...}, ...]
|
||
2. {"data": [...]} 或 {"items": [...]} 或 {"classList": [...]}
|
||
```
|
||
|
||
**注意**:如果 `class search` 返回 0 条记录,不一定是错误——可能是当前账号没有班次管理权限。此时从 `group get` 的 `shiftVOList` 中也可获取班次名称。
|
||
|
||
### `dws aisearch person` 搜索同名问题
|
||
|
||
同一个姓名可能返回**多个不同 userId**(如主管理员账号 vs 子管理员账号)。必须通过以下方式确认正确的 userId:
|
||
1. 检查目标考勤组的成员列表:`dws attendance group filtered-get --group-id <id> --member`
|
||
2. 取成员列表中存在的那个 userId
|
||
|
||
**禁止直接取搜索结果的第一条 userId,必须与考勤组成员列表交叉验证。**
|
||
|
||
### `dws contact user get` 可能的权限错误
|
||
|
||
`resolve_user_names`(userId→姓名转换)可能遇到 `SECURITY_CHECK_INVOKE_FAILED` 错误。这只影响**展示层**,不影响排班数据的正确性:
|
||
- 脚本已内置降级处理:权限失败时直接使用 userId 替代姓名
|
||
- **不要因为姓名获取失败就中止排班流程**
|
||
|
||
### Agent 常见错误模式(严禁)
|
||
|
||
| 错误做法 | 正确做法 |
|
||
|------------|------------|
|
||
| `result.get("type")` 从顶层取类型 | `result["groupVO"]["type"]` |
|
||
| `result.get("classIds")` 从顶层取班次 | `result["groupVO"]["classIds"]` 或 `result["groupVO"]["shiftVOList"]` |
|
||
| 用 `python3 -c "..."` inline 脚本解析 JSON | 调用已有的 Python 脚本(`attendance_schedule_import.py`) |
|
||
| 人名搜到多个结果直接取第一个 | 与考勤组成员列表交叉验证 |
|
||
| 姓名获取失败就中止流程 | 降级用 userId 展示,继续执行排班 |
|
||
| 直接调用 `dws attendance schedule import` | 必须通过 `attendance_schedule_import.py` 脚本 |
|
||
|
||
## 错误处理
|
||
|
||
| 错误 | 原因 | 处理方式 |
|
||
|------|------|---------|
|
||
| 权限错误(403) | 当前账号非管理员 | 提示需要管理员权限,不要重试 |
|
||
| 考勤组不是排班制 | 考勤组类型为 FIXED 或 NONE | 提示"该考勤组不是排班制,无法排班" |
|
||
| 班次不在可用列表中 | classId 无效 | 列出可用班次让用户重新选择 |
|
||
| userId 无效 | 用户 ID 错误或已离职 | 提示具体哪个用户无效 |
|
||
| 脚本执行失败 | 接口异常/配置问题 | 将 stderr 错误信息转告用户 |
|
||
| SECURITY_CHECK_INVOKE_FAILED | userId→姓名转换权限不足 | 仅影响展示,降级用 userId,不中止流程 |
|
||
| class search 返回空列表 | 账号无班次管理权限 | 从 `group get` 的 `shiftVOList` 提取班次名称 |
|
||
|
||
## 使用示例
|
||
|
||
### 示例 1: 给指定员工排班
|
||
**用户说**: "帮我给张三下周一到周五排早班,考勤组是研发组"
|
||
|
||
```bash
|
||
# 1. 确认考勤组并获取关联班次(先查后排)
|
||
dws attendance group search --query "研发组" --type TURN --format json
|
||
# → 拿到 groupId
|
||
dws attendance group get --group-id <groupId> --format json
|
||
# → 从 groupVO.shiftVOList 获取关联班次列表
|
||
# → 在 shiftVOList 中匹配"早班",拿到对应的 shiftId 作为 classId
|
||
# ⚠️ 禁止用 class search 全局搜索班次
|
||
|
||
# 2. 获取员工 userId
|
||
dws aisearch person --query "张三" --dimension name --format json
|
||
|
||
# 3. 回显确认(Agent 向用户展示排班表格)
|
||
# ... 用户确认 ...
|
||
|
||
# 4. 调用脚本执行
|
||
python scripts/attendance_schedule_import.py \
|
||
--group-id 123456 \
|
||
--schedules '[{"userId":"user001","workDate":"2026-05-19","classId":789,"isRest":"N"},{"userId":"user001","workDate":"2026-05-20","classId":789,"isRest":"N"},{"userId":"user001","workDate":"2026-05-21","classId":789,"isRest":"N"},{"userId":"user001","workDate":"2026-05-22","classId":789,"isRest":"N"},{"userId":"user001","workDate":"2026-05-23","classId":789,"isRest":"N"}]' \
|
||
--confirm
|
||
```
|
||
|
||
### 示例 2: 给员工排休
|
||
**用户说**: "帮我把李四下周三排休"
|
||
|
||
```bash
|
||
# 1. 先查考勤组和关联班次(即使排休也需要确认考勤组)
|
||
dws attendance rules --date 2026-05-15 --format json
|
||
# → 拿到 groupId
|
||
dws attendance group get --group-id <groupId> --format json
|
||
# → 确认是排班制(TURN)
|
||
|
||
# 2. 获取员工 userId
|
||
dws aisearch person --query "李四" --dimension name --format json
|
||
|
||
# 3. 回显确认 → 用户确认 → 执行
|
||
python scripts/attendance_schedule_import.py \
|
||
--group-id 123456 \
|
||
--schedules '[{"userId":"user002","workDate":"2026-05-21","classId":0,"isRest":"Y"}]' \
|
||
--confirm
|
||
```
|
||
|
||
### 示例 3: 部门批量排班
|
||
**用户说**: "帮我给研发部全员下周排早班"
|
||
|
||
```bash
|
||
# 1. 先查考勤组并获取关联班次(先查后排)
|
||
dws attendance rules --date 2026-05-15 --format json
|
||
# → 拿到 groupId
|
||
dws attendance group get --group-id <groupId> --format json
|
||
# → 从 groupVO.shiftVOList 中匹配"早班",拿到 shiftId
|
||
# ⚠️ 禁止用 class search 全局搜索班次
|
||
|
||
# 2. 获取部门成员
|
||
dws contact dept search --query "研发部" --format json
|
||
dws contact dept list-members --ids <deptId> --format json
|
||
|
||
# 3. 回显确认 → 用户确认 → 执行
|
||
python scripts/attendance_schedule_import.py \
|
||
--group-id 123456 \
|
||
--schedules '[...]' \
|
||
--confirm
|
||
```
|
||
|
||
## 配套脚本
|
||
|
||
| 脚本 | 用途 | CLI 参数 |
|
||
|------|------|---------|
|
||
| [attendance_schedule_import.py](../scripts/attendance_schedule_import.py) | 排班导入(含校验、回显、执行) | `--group-id --schedules --confirm` |
|
||
| [attendance_schedule_export.py](../scripts/attendance_schedule_export.py) | 排班查询导出(分批查询、排班表 Excel) | `--users --start --end [--output]` |
|
||
|
||
---
|
||
|
||
## 排班查询导出工作流
|
||
|
||
> 当用户提到"查看排班"、"排班表"、"导出排班"、"排班记录"时,走此工作流。
|
||
> **禁止直接调用 `dws attendance schedule get`**,必须通过脚本执行,脚本自动处理分批、姓名转换、班次名称转换、排班表格式输出。
|
||
|
||
### 查询导出 — 阶段 1: 参数收集
|
||
|
||
1. **员工范围**(必填):需获取 userId 列表
|
||
- 指定员工姓名 → `dws aisearch person` 获取 userId
|
||
- 指定部门 → `dws contact dept search` + `dws contact dept list-members`
|
||
- 指定考勤组全员 → `dws attendance group filtered-get --member`
|
||
- 用户已给 userId 列表 → 直接使用
|
||
2. **日期范围**(必填):开始日期 ~ 结束日期(YYYY-MM-DD)
|
||
- 用户说"下周" → 计算下周一到周日
|
||
- 用户说"本月" → 计算本月 1 日到月末
|
||
- 任何缺失信息必须追问
|
||
|
||
### 查询导出 — 阶段 2: 调用脚本
|
||
|
||
```bash
|
||
python scripts/attendance_schedule_export.py \
|
||
--users <userId1,userId2,...> \
|
||
--start <YYYY-MM-DD> \
|
||
--end <YYYY-MM-DD> \
|
||
[--output <output_path.xlsx>]
|
||
```
|
||
|
||
参数说明:
|
||
- `--users`(必填):userId 列表,逗号分隔
|
||
- `--start`(必填):开始日期,格式 YYYY-MM-DD
|
||
- `--end`(必填):结束日期,格式 YYYY-MM-DD
|
||
- `--output`(可选):输出文件路径,默认 `attendance_schedule_<start>_<end>.xlsx`
|
||
|
||
脚本内部自动处理:
|
||
1. **分批查询**:超过 20 人自动分批调用 `dws attendance schedule get`
|
||
2. **班次名称转换**:classId → className(优先从记录中提取,缺失时回退 class search)
|
||
3. **姓名转换**:userId → 员工姓名
|
||
4. **排班表格式**:日历表(行=员工,列=日期,单元格=班次名称)
|
||
5. **Excel 输出**:钉钉风格美化排版
|
||
|
||
### 查询导出 — 阶段 3: 返回结果给用户
|
||
|
||
- 将脚本 stdout 输出的摘要信息(人数、日期、记录数、预览表格)原样转告用户
|
||
- 提醒用户完整排班表已导出到 Excel 文件
|
||
- 如果执行失败,将 stderr 错误信息转告用户
|
||
|
||
### 查询导出示例
|
||
|
||
**用户说**: "帮我导出研发组下周的排班表"
|
||
|
||
```bash
|
||
# 1. 获取考勤组成员
|
||
dws attendance group search --query "研发组" --format json
|
||
dws attendance group filtered-get --group-id <groupId> --member --format json
|
||
|
||
# 2. 调用脚本导出
|
||
python scripts/attendance_schedule_export.py \
|
||
--users user001,user002,user003 \
|
||
--start 2026-05-18 \
|
||
--end 2026-05-24
|
||
```
|
||
|
||
**用户说**: "帮我查看张三和李四本月的排班"
|
||
|
||
```bash
|
||
# 1. 获取 userId
|
||
dws aisearch person --query "张三" --dimension name --format json
|
||
dws aisearch person --query "李四" --dimension name --format json
|
||
|
||
# 2. 调用脚本导出
|
||
python scripts/attendance_schedule_export.py \
|
||
--users user001,user002 \
|
||
--start 2026-05-01 \
|
||
--end 2026-05-31
|
||
```
|