skill:
name: 合并Excel工作簿
description: 将多个Excel工作簿合并为一个新工作簿,完全保留各工作表的格式和名称。支持通过弹窗选择文件或使用对话中已添加的文件。
actions:
- trigger:
- 合并工作簿
- 合并excel工作簿
- 合并添加的.*个excel工作簿
action: merge_excel_workbooks
user_prompt: true
parameters:
- name: file_paths
type: array
description: 要合并的Excel文件路径列表。如果用户未在对话中添加文件,则触发弹窗让用户选择。
required: true
- name: save_path
type: string
description: 合并后新工作簿的保存路径。由用户通过弹窗选择保存位置。
required: true
code: |
import os
import sys
import tkinter as tk
from tkinter import filedialog, messagebox
import openpyxl
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter
import traceback
def merge_workbooks(file_paths, save_path):
"""
合并多个Excel工作簿,完全保留每个工作表的格式和名称。
"""
if not file_paths:
raise ValueError("未选择任何要合并的文件。")
# 加载第一个工作簿作为基础
base_wb = load_workbook(file_paths[0])
# 记录所有工作表名称,避免重复
sheet_names = set(base_wb.sheetnames)
# 遍历其余工作簿,将工作表添加到基础工作簿中
for file_path in file_paths[1:]:
try:
wb = load_workbook(file_path)
for sheet_name in wb.sheetnames:
# 如果工作表名已存在,则添加后缀
new_sheet_name = sheet_name
counter = 1
while new_sheet_name in sheet_names:
new_sheet_name = f"{sheet_name}_{counter}"
counter += 1
# 复制工作表
source_sheet = wb[sheet_name]
target_sheet = base_wb.create_sheet(new_sheet_name)
# 复制所有单元格的值和样式
for row in source_sheet.iter_rows():
for cell in row:
new_cell = target_sheet.cell(row=cell.row, column=cell.column)
new_cell.value = cell.value
if cell.has_style:
new_cell.font = cell.font.copy()
new_cell.border = cell.border.copy()
new_cell.fill = cell.fill.copy()
new_cell.number_format = cell.number_format
new_cell.protection = cell.protection.copy()
new_cell.alignment = cell.alignment.copy()
# 复制列宽和行高
for col in source_sheet.column_dimensions:
target_sheet.column_dimensions[col].width = source_sheet.column_dimensions[col].width
for row in source_sheet.row_dimensions:
target_sheet.row_dimensions[row].height = source_sheet.row_dimensions[row].height
# 复制合并单元格
for merged_range in source_sheet.merged_cells.ranges:
target_sheet.merge_cells(str(merged_range))
sheet_names.add(new_sheet_name)
except Exception as e:
raise Exception(f"处理文件 {file_path} 时出错: {str(e)}")
# 保存合并后的工作簿
try:
base_wb.save(save_path)
return f"成功合并 {len(file_paths)} 个工作簿,保存至: {save_path}"
except Exception as e:
raise Exception(f"保存文件时出错: {str(e)}")
def select_files():
"""弹出文件选择对话框,让用户选择Excel文件"""
root = tk.Tk()
root.withdraw() # 隐藏主窗口
file_paths = filedialog.askopenfilenames(
title="选择要合并的Excel工作簿",
filetypes=[("Excel文件", "*.xlsx *.xlsm *.xltx *.xltm"), ("所有文件", "*.*")]
)
return list(file_paths)
def select_save_path():
"""弹出保存对话框,让用户选择保存位置"""
root = tk.Tk()
root.withdraw()
save_path = filedialog.asksaveasfilename(
title="保存合并后的工作簿",
defaultextension=".xlsx",
filetypes=[("Excel工作簿", "*.xlsx"), ("Excel启用宏的工作簿", "*.xlsm")]
)
return save_path
def main(params):
"""
主执行函数,由Work Buddy调用。
params: 包含 file_paths 和 save_path 的字典
"""
try:
# 获取文件路径列表
file_paths = params.get('file_paths')
save_path = params.get('save_path')
# 如果未提供文件路径或为空,则弹出选择对话框
if not file_paths:
file_paths = select_files()
if not file_paths:
return "未选择任何文件,操作取消。"
# 如果未提供保存路径,则弹出保存对话框
if not save_path:
save_path = select_save_path()
if not save_path:
return "未选择保存位置,操作取消。"
# 执行合并
result = merge_workbooks(file_paths, save_path)
messagebox.showinfo("合并完成", result)
return result
except Exception as e:
error_msg = f"合并过程中发生错误: {str(e)}\n{traceback.format_exc()}"
messagebox.showerror("错误", error_msg)
return error_msg
# 如果脚本直接运行,执行主函数(用于测试)
if __name__ == "__main__":
main({})
dependencies:
- openpyxl
- tkinter
python_version: ">=3.6"
environment: embedded
exec_mode: python
timeout: 300
retry: 0
output_behavior: text
need_upload: true
upload_file_types:
- .xlsx
- .xlsm
- .xltx
- .xltm
help: |
此技能用于合并多个Excel工作簿为一个新工作簿,并完全保留所有工作表的格式和名称。
使用方式:
1. 在对话中触发关键词“合并工作簿”或“合并excel工作簿”。
2. 如果已通过“+”号添加了Excel文件,则直接使用这些文件进行合并。
3. 如果未添加文件,则会自动弹出文件选择对话框,供您浏览并选择要合并的文件。
4. 随后会弹出保存对话框,让您选择合并后新工作簿的保存位置。
注意:
- 合并后的工作簿中,每个工作表将以原工作簿中的工作表名称命名。
- 如果存在同名工作表,会自动添加数字后缀以区分。
- 所有单元格的值、样式、列宽、行高和合并单元格都会被完整保留。
- 无需安装Python环境,Work Buddy内置运行环境。
支付宝付费改造契约(PAYSKILL)
本技能为付费技能(PAYSKILL)。调用遵循支付宝 AI 生意画像契约,由平台调度层执行 probe → pay → complete → ack 流程:
- probe(探测):平台调用前先探测是否已持有本技能的支付宝有效订阅 / 付费凭证。已付费则直接放行。
- 402 Payment-Needed:未付费时,脚本向标准输出返回 HTTP 状态
402与Payment-Needed标记,提示用户通过支付宝完成付费(pay),平台据此唤起付费页。 - complete(扣费确认):用户在支付宝完成扣费后,平台下发
Payment-Proof(付费凭证)。 - ack(验签放行):平台携带
Payment-Proof重试本次调用;scripts/payment_gate.py验签该凭证,确认有效后放行主流程。
订阅持久化与幂等:
- 付费状态缓存在本地
.pay_cache/state.json,避免每次调用重复付费(订阅持久化)。 - 同一
Payment-Proof在 24h 幂等窗口内仅生效一次,避免重复扣费。
现金流改造后的调用套路:
用户调用 → 平台 probe →
未付费:下发 402 Payment-Needed → 支付宝 pay → complete → 下发 Payment-Proof
已付费/携带 Proof:脚本 ack 验签 → 放行主流程
> 本契约由 SkillHUB 平台调度层解析,脚本本地仅处理 402 / 验签 / 持久化 三环节;付费本体交给支付宝。
支付宝 AI 按量付费(Machine Pay / A2M)改造说明
本 Skill 已按支付宝官方 A2M 协议改造为 Pay Skill,付费闸口位于 scripts/payment_gate.py,统一调用云端计费服务 https://pay.pembroli.cn/api/resource。
付费流程
- 未付费调用时,服务端返回 HTTP 402 +
Payment-Needed头,携带金额、商户、订单号及 RSA2 签名。 - 买家侧
payment_gate.py收到 402 后引导用户完成支付,平台回传Payment-Proof。 - 客户端携带
Payment-Proof重试资源端点。
验付调用
服务端收到 Payment-Proof 后,调用支付宝验付接口(alipay.aipay.agent.payment.verify)核验凭证有效性(trade_no、out_trade_no、active 状态),验证失败返回 400,验证通过才进入资源发放。
履约确认
资源发放成功后,服务端异步调用履约确认接口(alipay.aipay.agent.fulfillment.confirm,trade_no)回执,标记本笔订单已履约;并按 out_trade_no 做幂等,重复请求直接返回已缓存结果,杜绝重复收费。
幂等与缓存
买家侧按 slug 本地缓存 Payment-Proof(24h TTL),有效期内免重复支付;服务端按订单号幂等,保证同一笔订单只履约一次。