feat(tools): 添加获取用户OKR列表和批量查询OKR详情的工具
新增两个与飞书OKR API集成的工具函数: - get_user_okr_list: 根据用户ID分页查询其OKR列表,支持按周期筛选和语言选择 - batch_get_okr: 根据OKR ID批量获取详细的目标及关键结果信息,并格式化输出进度、状态、截止日期等关键信息 同时在handle_call_tool中添加对应的调用处理逻辑。
This commit is contained in:
@@ -189,6 +189,62 @@ tools = [
|
|||||||
"required": ["user_id", "order_number", "asset_list", "face_cap", "user_ids"]
|
"required": ["user_id", "order_number", "asset_list", "face_cap", "user_ids"]
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
Tool(
|
||||||
|
name="get_user_okr_list",
|
||||||
|
description="根据用户user_id获取该用户的OKR列表,返回OKR ID及目标(Objective)和关键结果(Key Result)详情",
|
||||||
|
inputSchema={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"user_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "用户的user_id"
|
||||||
|
},
|
||||||
|
"offset": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "分页偏移,默认0"
|
||||||
|
},
|
||||||
|
"limit": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "每页数量,范围0-10,默认10"
|
||||||
|
},
|
||||||
|
"lang": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "语言版本: zh_cn 或 en_us,默认 zh_cn",
|
||||||
|
"enum": ["zh_cn", "en_us"]
|
||||||
|
},
|
||||||
|
"period_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "OKR周期ID列表,最多10个,不传则返回所有周期",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["user_id"]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Tool(
|
||||||
|
name="batch_get_okr",
|
||||||
|
description="根据OKR ID批量获取OKR详情,返回目标(Objective)及其关键结果(Key Result)详情",
|
||||||
|
inputSchema={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"okr_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "OKR ID列表,最多10个",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lang": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "语言版本: zh_cn 或 en_us,默认 zh_cn",
|
||||||
|
"enum": ["zh_cn", "en_us"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["okr_ids"]
|
||||||
|
}
|
||||||
|
),
|
||||||
Tool(
|
Tool(
|
||||||
name="send_feedback_card",
|
name="send_feedback_card",
|
||||||
description="发送资产变动反馈单,入参为receiver_ids、user_id、order_number、change_time、asset_list和remark",
|
description="发送资产变动反馈单,入参为receiver_ids、user_id、order_number、change_time、asset_list和remark",
|
||||||
@@ -417,6 +473,107 @@ def _build_asset_options(inputs: List[str], outputs: List[str]) -> tuple[list[Di
|
|||||||
return options, "、".join(labels)
|
return options, "、".join(labels)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_okr_list(
|
||||||
|
token: str,
|
||||||
|
user_id: str,
|
||||||
|
offset: str = "0",
|
||||||
|
limit: str = "10",
|
||||||
|
lang: str = "zh_cn",
|
||||||
|
period_ids: List[str] | None = None,
|
||||||
|
) -> str:
|
||||||
|
url = f"https://open.feishu.cn/open-apis/okr/v1/users/{user_id}/okrs"
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
params: Dict[str, object] = {
|
||||||
|
"user_id_type": "user_id",
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
"lang": lang,
|
||||||
|
}
|
||||||
|
if period_ids:
|
||||||
|
params["period_ids"] = period_ids
|
||||||
|
response = requests.get(url, headers=headers, params=params, timeout=15)
|
||||||
|
response.raise_for_status()
|
||||||
|
payload = response.json()
|
||||||
|
if payload.get("code") not in (0, None):
|
||||||
|
raise RuntimeError(f"获取用户OKR列表失败: {payload}")
|
||||||
|
data = payload.get("data", {})
|
||||||
|
total = data.get("total", 0)
|
||||||
|
okr_list = data.get("okr_list", [])
|
||||||
|
return json.dumps({"total": total, "okr_list": okr_list}, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
def batch_get_okr(
|
||||||
|
token: str,
|
||||||
|
okr_ids: List[str],
|
||||||
|
lang: str = "zh_cn",
|
||||||
|
) -> str:
|
||||||
|
url = "https://open.feishu.cn/open-apis/okr/v1/okrs/batch_get"
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
params: Dict[str, object] = {
|
||||||
|
"okr_ids": okr_ids,
|
||||||
|
"user_id_type": "user_id",
|
||||||
|
"lang": lang,
|
||||||
|
}
|
||||||
|
response = requests.get(url, headers=headers, params=params, timeout=15)
|
||||||
|
response.raise_for_status()
|
||||||
|
payload = response.json()
|
||||||
|
if payload.get("code") not in (0, None):
|
||||||
|
raise RuntimeError(f"批量获取OKR失败: {payload}")
|
||||||
|
data = payload.get("data", {})
|
||||||
|
okr_list = data.get("okr_list", [])
|
||||||
|
lines: List[str] = [f"共返回 {len(okr_list)} 条OKR记录:"]
|
||||||
|
status_map = {"-1": "暂无", "0": "正常", "1": "风险", "2": "延期"}
|
||||||
|
confirm_map = {0: "初始状态", 1: "待提交", 2: "待确认", 3: "已拒绝", 4: "已确认"}
|
||||||
|
for idx, okr in enumerate(okr_list, 1):
|
||||||
|
okr_name = okr.get("name") or okr.get("id", "")
|
||||||
|
confirm = okr.get("confirm_status")
|
||||||
|
confirm_text = confirm_map.get(confirm, str(confirm)) if confirm is not None else ""
|
||||||
|
lines.append(f"\n{'='*40}")
|
||||||
|
lines.append(f"OKR #{idx}: {okr_name} (周期ID: {okr.get('period_id', '')})")
|
||||||
|
if confirm_text:
|
||||||
|
lines.append(f" 确认状态: {confirm_text}")
|
||||||
|
objective_list = okr.get("objective_list", [])
|
||||||
|
for oi, obj in enumerate(objective_list, 1):
|
||||||
|
content = obj.get("content", "")
|
||||||
|
score = obj.get("score", "")
|
||||||
|
weight = obj.get("weight", "")
|
||||||
|
progress_report = obj.get("progress_report", "")
|
||||||
|
progress = obj.get("progress_rate") or {}
|
||||||
|
percent = progress.get("percent", "")
|
||||||
|
status = status_map.get(str(progress.get("status", "")), "")
|
||||||
|
deadline = obj.get("deadline", "")
|
||||||
|
lines.append(f" O{oi}: {content}")
|
||||||
|
if weight:
|
||||||
|
lines.append(f" 权重: {weight}%")
|
||||||
|
if score:
|
||||||
|
lines.append(f" 评分: {score}")
|
||||||
|
if percent != "":
|
||||||
|
lines.append(f" 进度: {percent}%{(' (' + status + ')') if status else ''}")
|
||||||
|
if deadline and deadline != "0":
|
||||||
|
lines.append(f" 截止: {datetime.fromtimestamp(int(deadline) / 1000, tz=timezone(timedelta(hours=8))).strftime('%Y-%m-%d')}")
|
||||||
|
if progress_report:
|
||||||
|
lines.append(f" 备注: {progress_report}")
|
||||||
|
kr_list = obj.get("kr_list", [])
|
||||||
|
for ki, kr in enumerate(kr_list, 1):
|
||||||
|
kr_content = kr.get("content", "")
|
||||||
|
kr_score = kr.get("score", "")
|
||||||
|
kr_weight = kr.get("kr_weight", "")
|
||||||
|
kr_deadline = kr.get("deadline", "")
|
||||||
|
kr_progress = kr.get("progress_rate") or {}
|
||||||
|
kr_percent = kr_progress.get("percent", "")
|
||||||
|
kr_status = status_map.get(str(kr_progress.get("status", "")), "")
|
||||||
|
lines.append(f" KR{ki}: {kr_content}")
|
||||||
|
if kr_weight:
|
||||||
|
lines.append(f" 权重: {kr_weight}%")
|
||||||
|
if kr_score:
|
||||||
|
lines.append(f" 评分: {kr_score}")
|
||||||
|
if kr_percent != "":
|
||||||
|
lines.append(f" 进度: {kr_percent}%{(' (' + kr_status + ')') if kr_status else ''}")
|
||||||
|
if kr_deadline and kr_deadline != "0":
|
||||||
|
lines.append(f" 截止: {datetime.fromtimestamp(int(kr_deadline) / 1000, tz=timezone(timedelta(hours=8))).strftime('%Y-%m-%d')}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def _build_select_options(items: List[str]) -> list[Dict[str, object]]:
|
def _build_select_options(items: List[str]) -> list[Dict[str, object]]:
|
||||||
options: list[Dict[str, object]] = []
|
options: list[Dict[str, object]] = []
|
||||||
for item in items:
|
for item in items:
|
||||||
@@ -1883,6 +2040,39 @@ async def handle_call_tool(name: str, arguments: Dict[str, object], token: str)
|
|||||||
user_ids,
|
user_ids,
|
||||||
remark
|
remark
|
||||||
)
|
)
|
||||||
|
elif name == "get_user_okr_list":
|
||||||
|
user_id = str(arguments.get("user_id", "")).strip()
|
||||||
|
if not user_id:
|
||||||
|
raise ValueError("missing user_id")
|
||||||
|
offset = str(arguments.get("offset") or "0").strip()
|
||||||
|
limit = str(arguments.get("limit") or "10").strip()
|
||||||
|
lang = str(arguments.get("lang") or "zh_cn").strip()
|
||||||
|
period_ids = arguments.get("period_ids")
|
||||||
|
if period_ids is not None and not isinstance(period_ids, list):
|
||||||
|
period_ids = None
|
||||||
|
result = get_user_okr_list(
|
||||||
|
token,
|
||||||
|
user_id,
|
||||||
|
offset=offset,
|
||||||
|
limit=limit,
|
||||||
|
lang=lang,
|
||||||
|
period_ids=period_ids,
|
||||||
|
)
|
||||||
|
elif name == "batch_get_okr":
|
||||||
|
okr_ids = arguments.get("okr_ids")
|
||||||
|
if not okr_ids or not isinstance(okr_ids, list):
|
||||||
|
raise ValueError("missing or invalid okr_ids, must be a list of OKR ID strings")
|
||||||
|
if len(okr_ids) > 10:
|
||||||
|
raise ValueError("okr_ids supports at most 10 items")
|
||||||
|
okr_ids = [str(oid).strip() for oid in okr_ids if str(oid).strip()]
|
||||||
|
if not okr_ids:
|
||||||
|
raise ValueError("okr_ids is empty after filtering")
|
||||||
|
lang = str(arguments.get("lang") or "zh_cn").strip()
|
||||||
|
result = batch_get_okr(
|
||||||
|
token,
|
||||||
|
okr_ids,
|
||||||
|
lang=lang,
|
||||||
|
)
|
||||||
elif name == "send_feedback_card":
|
elif name == "send_feedback_card":
|
||||||
user_id = str(arguments.get("user_id", "")).strip()
|
user_id = str(arguments.get("user_id", "")).strip()
|
||||||
order_number = str(arguments.get("order_number", "")).strip()
|
order_number = str(arguments.get("order_number", "")).strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user