feat(lark-mcp): 重构资产确认卡片并更新项目配置
重构 send_asset_confirmation_card 工具,使用新的卡片模板和参数结构 - 将卡片布局从 column_set 改为 form,简化结构 - 更新参数:inputs/outputs 合并为 asset_list,新增 order_number 和 remark - 卡片交互按钮现在传递 order_number 和 user_id 到回调 - 更新工具描述和必填字段 同时更新 MCP 服务器配置: - 统一重命名项目为 lzwcai-lark-mcp 和 lzwcai-file-tools-mcp - 更新版本号和配置文件中的命令名称 - 更新 file-tools 的 MinIO 环境变量配置
This commit is contained in:
@@ -2,7 +2,7 @@ import json
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Dict, List
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@@ -77,7 +77,7 @@ tools = [
|
||||
),
|
||||
Tool(
|
||||
name="send_asset_confirmation_card",
|
||||
description="发送资产变动确认卡片,入参为user_id、time、inputs和outputs",
|
||||
description="发送定制资产确认卡片,入参为user_id、order_number、change_time、asset_list和remark",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -85,26 +85,27 @@ tools = [
|
||||
"type": "string",
|
||||
"description": "消息接收者ID,同时用于卡片内person组件"
|
||||
},
|
||||
"time": {
|
||||
"order_number": {
|
||||
"type": "string",
|
||||
"description": "确认单号"
|
||||
},
|
||||
"change_time": {
|
||||
"type": "string",
|
||||
"description": "资产变动发生时间"
|
||||
},
|
||||
"inputs": {
|
||||
"asset_list": {
|
||||
"type": "array",
|
||||
"description": "入库资产列表",
|
||||
"description": "可选择的资产变动项列表",
|
||||
"items": {
|
||||
"type": "string"
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"type": "array",
|
||||
"description": "出库资产列表",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
"remark": {
|
||||
"type": "string",
|
||||
"description": "反馈确认弹窗内容"
|
||||
}
|
||||
},
|
||||
"required": ["user_id", "time"]
|
||||
"required": ["user_id", "order_number"]
|
||||
}
|
||||
)
|
||||
]
|
||||
@@ -280,16 +281,53 @@ def _build_asset_options(inputs: List[str], outputs: List[str]) -> tuple[list[Di
|
||||
return options, "、".join(labels)
|
||||
|
||||
|
||||
def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inputs: object, outputs: object) -> str:
|
||||
def _build_select_options(items: List[str]) -> list[Dict[str, object]]:
|
||||
options: list[Dict[str, object]] = []
|
||||
for item in items:
|
||||
label = str(item).strip()
|
||||
if not label:
|
||||
continue
|
||||
options.append({
|
||||
"text": {"tag": "plain_text", "content": label},
|
||||
"value": label
|
||||
})
|
||||
return options
|
||||
|
||||
|
||||
def send_asset_confirmation_card(
|
||||
token: str,
|
||||
user_id: str,
|
||||
order_number: str,
|
||||
change_time: str,
|
||||
asset_list: object,
|
||||
remark: object = ""
|
||||
) -> str:
|
||||
normalized_user_id = str(user_id).strip()
|
||||
if not normalized_user_id:
|
||||
raise ValueError("missing user_id")
|
||||
normalized_order_number = str(order_number).strip()
|
||||
if not normalized_order_number:
|
||||
raise ValueError("missing order_number")
|
||||
normalized_time = str(change_time or "").strip()
|
||||
if not normalized_time:
|
||||
normalized_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
input_items = _normalize_asset_items(inputs)
|
||||
output_items = _normalize_asset_items(outputs)
|
||||
options, summary = _build_asset_options(input_items, output_items)
|
||||
normalized_remark = str(remark or "").strip()
|
||||
|
||||
options = []
|
||||
if isinstance(asset_list, list):
|
||||
for item in asset_list:
|
||||
if isinstance(item, dict):
|
||||
for k, v in item.items():
|
||||
options.append({
|
||||
"text": {"tag": "plain_text", "content": str(k)},
|
||||
"value": str(v)
|
||||
})
|
||||
elif isinstance(item, str):
|
||||
options.append({
|
||||
"text": {"tag": "plain_text", "content": item},
|
||||
"value": item
|
||||
})
|
||||
|
||||
content = {
|
||||
"schema": "2.0",
|
||||
"config": {
|
||||
@@ -307,57 +345,30 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
|
||||
"body": {
|
||||
"direction": "vertical",
|
||||
"elements": [
|
||||
{
|
||||
"tag": "column_set",
|
||||
"flex_mode": "stretch",
|
||||
"horizontal_spacing": "12px",
|
||||
"horizontal_align": "left",
|
||||
"columns": [
|
||||
{
|
||||
"tag": "column",
|
||||
"width": "weighted",
|
||||
"elements": [
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": f"**<font color='blue-600'>用户:</font>** <person id={normalized_user_id} show_name=true show_avatar=true style='normal'></person>",
|
||||
"text_align": "left",
|
||||
"text_size": "normal"
|
||||
}
|
||||
],
|
||||
"vertical_spacing": "8px",
|
||||
"horizontal_align": "left",
|
||||
"vertical_align": "top",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"tag": "column",
|
||||
"width": "weighted",
|
||||
"elements": [
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": f"**<font color='blue-600'>发生时间:</font>** <font color='grey'>{normalized_time}</font>",
|
||||
"text_align": "left",
|
||||
"text_size": "normal"
|
||||
}
|
||||
],
|
||||
"vertical_spacing": "8px",
|
||||
"horizontal_align": "left",
|
||||
"vertical_align": "top",
|
||||
"weight": 1
|
||||
}
|
||||
],
|
||||
"margin": "0px 0px 0px 0px"
|
||||
},
|
||||
{
|
||||
"tag": "hr",
|
||||
"margin": "0px 0px 0px 0px"
|
||||
},
|
||||
{
|
||||
"tag": "form",
|
||||
"elements": [
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": "**<font color='blue-600'>\\*请准确选择属于您的变动项:</font>**",
|
||||
"content": f"**<font color='blue-600'>确认单号:</font>** <font color='grey'>{normalized_order_number}</font>",
|
||||
"text_align": "left",
|
||||
"text_size": "normal"
|
||||
},
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": f"**<font color='blue-600'>用户:</font>** <person id={normalized_user_id} show_name=true show_avatar=true style='normal'></person>",
|
||||
"text_align": "left",
|
||||
"text_size": "normal"
|
||||
},
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": f"**<font color='blue-600'>发生时间:</font>** <font color='grey'>{normalized_time}</font>",
|
||||
"text_align": "left",
|
||||
"text_size": "normal"
|
||||
},
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": "**<font color='blue-600'>\\*请准确选择关于您的变动项:</font>**",
|
||||
"text_align": "left",
|
||||
"text_size": "normal",
|
||||
"margin": "0px 0px 8px 0px"
|
||||
@@ -372,10 +383,14 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
|
||||
"type": "default",
|
||||
"width": "fill",
|
||||
"required": False,
|
||||
"name": "asset_changes",
|
||||
"name": "input_assets",
|
||||
"margin": "0px 0px 16px 0px",
|
||||
"element_id": "cIiptD7Z4hCtAeR5Rb0b"
|
||||
},
|
||||
{
|
||||
"tag": "hr",
|
||||
"margin": "0px 0px 0px 0px"
|
||||
},
|
||||
{
|
||||
"tag": "markdown",
|
||||
"content": "**<font color='blue-600'>其他说明:</font>**",
|
||||
@@ -391,7 +406,7 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
|
||||
},
|
||||
"default_value": "",
|
||||
"width": "fill",
|
||||
"name": "Input_3h27n7woqci",
|
||||
"name": "input_remark",
|
||||
"margin": "0px 0px 0px 0px"
|
||||
},
|
||||
{
|
||||
@@ -417,7 +432,9 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
|
||||
{
|
||||
"type": "callback",
|
||||
"value": {
|
||||
"action": "confirm_asset_changes"
|
||||
"action": "card.action.trigger",
|
||||
"order_number": normalized_order_number,
|
||||
"user_id": normalized_user_id
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -445,18 +462,20 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
|
||||
"confirm": {
|
||||
"title": {
|
||||
"tag": "plain_text",
|
||||
"content": "反馈并废除该确认单吗?"
|
||||
"content": "反馈误报并废除该确认单吗?"
|
||||
},
|
||||
"text": {
|
||||
"tag": "plain_text",
|
||||
"content": summary
|
||||
"content": normalized_remark
|
||||
}
|
||||
},
|
||||
"behaviors": [
|
||||
{
|
||||
"type": "callback",
|
||||
"value": {
|
||||
"": ""
|
||||
"action": "card.action.trigger",
|
||||
"order_number": normalized_order_number,
|
||||
"user_id": normalized_user_id
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -543,7 +562,9 @@ def send_asset_confirmation_card(token: str, user_id: str, change_time: str, inp
|
||||
|
||||
|
||||
def send_card_message(token: str, receiver_id: str, person_id: str, image_key: str) -> str:
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
utc_now = datetime.now(timezone.utc)
|
||||
cst_now = utc_now + timedelta(hours=8)
|
||||
current_time = cst_now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
content = {
|
||||
"schema": "2.0",
|
||||
"config": {
|
||||
@@ -746,12 +767,22 @@ async def handle_call_tool(name: str, arguments: Dict[str, object], token: str)
|
||||
result = send_card_messages(token, receiver_ids, person_id, image_key)
|
||||
elif name == "send_asset_confirmation_card":
|
||||
user_id = str(arguments.get("user_id", "")).strip()
|
||||
change_time = str(arguments.get("time", "")).strip()
|
||||
inputs = arguments.get("inputs")
|
||||
outputs = arguments.get("outputs")
|
||||
order_number = str(arguments.get("order_number", "")).strip()
|
||||
change_time = str(arguments.get("change_time", "")).strip()
|
||||
asset_list = arguments.get("asset_list")
|
||||
remark = str(arguments.get("remark", "")).strip()
|
||||
if not user_id:
|
||||
raise ValueError("missing user_id")
|
||||
result = send_asset_confirmation_card(token, user_id, change_time, inputs, outputs)
|
||||
if not order_number:
|
||||
raise ValueError("missing order_number")
|
||||
result = send_asset_confirmation_card(
|
||||
token,
|
||||
user_id,
|
||||
order_number,
|
||||
change_time,
|
||||
asset_list,
|
||||
remark
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"unknown tool name: {name}")
|
||||
return [TextContent(type="text", text=result)]
|
||||
|
||||
Reference in New Issue
Block a user