PRA-only-demo/scripts/gen-morning-flow.py
yu 902ee0cec4 File_File_Move 目标为已存在目录时自动移入该目录
此前 pathDest 被固定当作完整目标文件路径, 目标填分类目录时
rename(文件->目录)失败, fallback cp 报 ENOTDIR(cannot overwrite
directory with non-directory), 文件整理类流程无法用目录作目标。

现在目标存在且为目录时自动拼接源文件名(移入语义), 目标不存在
仍按完整文件路径处理。

附: 晨间助手全家桶测试流程生成脚本(30节点, 文件整理+记事本
键鼠+Excel加工+OCR/AI)
2026-11-05 16:05:07 +08:00

193 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""生成「全家桶 · 晨间自动化助手」流程 JSON。"""
import json
import os
BASE = r"C:\Users\Administrator\Desktop\RPA整理测试"
EXCEL = "C:/Users/Administrator/Desktop/淘宝手机比价.xlsx"
OUT_XLSX = "C:/Users/Administrator/Desktop/低价手机清单.xlsx"
SHOT = BASE + "\\整理完成截图.png"
nodes = []
edges = []
def node(id, componentId, label, values, output_var=None, timeout=30000, parent=None, slot=None, pos=None, options_extra=None):
options = {"timeoutMs": timeout}
if output_var:
options["outputVar"] = output_var
if options_extra:
options.update(options_extra)
n = {
"id": id,
"componentId": componentId,
"label": label,
"values": values,
"options": options,
}
if parent:
n["parentId"] = parent
n["slot"] = slot
n["position"] = {"x": 18, "y": 0}
else:
n["position"] = pos
nodes.append(n)
return n
def link(a, b):
edges.append({"id": f"edge_{a}_{b}", "source": a, "target": b})
# ============ 主链 ============
node("node_start", "Trigger.Manual", "立即执行", {"entryName": "立即执行"}, pos={"x": 240, "y": 70})
# ---- 板块 A:文件整理 ----
node("node_scan", "File_GetFileList_V1", "扫描测试文件夹", {
"folder_path": BASE, "key_word": "", "ignore": "",
"recursive": "0", "is_full_path": "1", "sort_type": "1",
}, output_var="dlFiles", pos={"x": 240, "y": 210})
node("node_mkdir_zip", "File_MakeDir", "建分类文件夹:压缩包", {"path": BASE + "\\压缩包", "name": "", "overwrite": "False"}, pos={"x": 240, "y": 340})
node("node_mkdir_img", "File_MakeDir", "建分类文件夹:图片", {"path": BASE + "\\图片", "name": "", "overwrite": "False"}, pos={"x": 240, "y": 460})
node("node_mkdir_doc", "File_MakeDir", "建分类文件夹:文档", {"path": BASE + "\\文档", "name": "", "overwrite": "False"}, pos={"x": 240, "y": 580})
node("node_mkdir_other", "File_MakeDir", "建分类文件夹:其他", {"path": BASE + "\\其他", "name": "", "overwrite": "False"}, pos={"x": 240, "y": 700})
node("node_loop", "logic_control_array_for_v3", "遍历文件逐个分类", {
"array_name": "dlFiles", "reverse": "0", "start_index": "0", "end_index": "-1",
}, pos={"x": 240, "y": 840})
# 循环体内:三层 IF 嵌套分类
node("node_if_zip", "logic_control_if_else", "是压缩包?", {
"condition": '${contains(lower(item), ".zip") || contains(lower(item), ".rar") || contains(lower(item), ".7z")}',
}, parent="node_loop", slot="array_for_fn")
node("node_move_zip", "File_File_Move", "移入「压缩包」", {
"file_type": "file", "file_path": "${item}", "target_path": BASE + "\\压缩包", "overwrite": "True",
}, parent="node_if_zip", slot="statement_if")
node("node_if_img", "logic_control_if_else", "是图片?", {
"condition": '${contains(lower(item), ".png") || contains(lower(item), ".jpg") || contains(lower(item), ".jpeg") || contains(lower(item), ".gif") || contains(lower(item), ".bmp")}',
}, parent="node_if_zip", slot="statement_else")
node("node_move_img", "File_File_Move", "移入「图片」", {
"file_type": "file", "file_path": "${item}", "target_path": BASE + "\\图片", "overwrite": "True",
}, parent="node_if_img", slot="statement_if")
node("node_if_doc", "logic_control_if_else", "是文档?", {
"condition": '${contains(lower(item), ".doc") || contains(lower(item), ".pdf") || contains(lower(item), ".txt") || contains(lower(item), ".xlsx") || contains(lower(item), ".ppt")}',
}, parent="node_if_img", slot="statement_else")
node("node_move_doc", "File_File_Move", "移入「文档」", {
"file_type": "file", "file_path": "${item}", "target_path": BASE + "\\文档", "overwrite": "True",
}, parent="node_if_doc", slot="statement_if")
node("node_move_other", "File_File_Move", "移入「其他」", {
"file_type": "file", "file_path": "${item}", "target_path": BASE + "\\其他", "overwrite": "True",
}, parent="node_if_doc", slot="statement_else")
# ---- 板块 B:Excel 数据加工 ----
node("node_read_excel", "Bivartable_DoOpenExcel", "读取淘宝比价 Excel", {
"filePath": EXCEL, "sheetName": "Sheet1", "indexx": "False",
}, output_var="priceTable", pos={"x": 240, "y": 990})
node("node_sort", "Bivartable_SortDf", "按价格升序排序", {
"df": "${priceTable}", "name": "价格", "sort": "True", "showTable": "0",
}, output_var="sortedTable", pos={"x": 240, "y": 1110})
node("node_filter", "Bivartable_FilterDf", "筛选 3000 元以下", {
"df": "${sortedTable}", "filter_mode": "ordinary", "col_name": "价格",
"choice": "4", "criteria": "3000", "rule": '["价格"] <= 3000', "showTable": "0",
}, output_var="filteredTable", pos={"x": 240, "y": 1230})
node("node_min", "Bivartable_GetMinValue", "算最低价", {
"df": "${priceTable}", "groupby": "", "minvaluecols": "价格",
}, output_var="minPrice", pos={"x": 240, "y": 1350})
node("node_max", "Bivartable_GetMaxValue", "算最高价", {
"df": "${priceTable}", "groupby": "", "maxvaluecols": "价格",
}, output_var="maxPrice", pos={"x": 240, "y": 1470})
node("node_avg", "Bivartable_GetAvgValue", "算平均价", {
"df": "${priceTable}", "groupby": "", "avgvaluecols": "价格",
}, output_var="avgPrice", pos={"x": 240, "y": 1590})
node("node_write_cheap", "Bivartable_DfWriteToFile", "保存低价清单 Excel", {
"df": "${filteredTable}", "suffix": "xlsx", "filePath": OUT_XLSX, "folderPath": "C:/Users/Administrator/Desktop", "sheet": "Sheet1",
}, pos={"x": 240, "y": 1710})
# ---- 板块 C:记事本键鼠 + 截图 ----
node("node_notepad_run", "App_Run", "启动记事本", {
"executeable": "notepad.exe", "params": "", "cwd": "", "is_admin": "0", "create_new_console": "0",
}, output_var="notepadPid", pos={"x": 240, "y": 1830})
node("node_delay_notepad", "Basic_Delay_V2", "等记事本就绪", {
"is_random": "0", "second": "1", "min_second": "1", "max_second": "5",
}, pos={"x": 240, "y": 1950})
summary_expr = (
'${"RPA 晨间助手报告 " + today() + ":已整理测试文件 " + len(dlFiles) + " 个;'
'比价表筛出 3000 元以下手机 " + (len(filteredTable) - 1) + " 款;'
'最低 " + minPrice + " 元,最高 " + maxPrice + " 元,平均 " + round(avgPrice, 1) + " 元。"}'
)
node("node_notepad_type", "Keyboard_Send_V1", "记事本输入摘要(键盘模拟)", {
"words": summary_expr, "interval": "10", "fuctype": "0", "window_title": "记事本",
}, pos={"x": 240, "y": 2070})
node("node_shot", "Image_Screenshot", "截屏留证", {
"pictureSource": "1", "shotRange": "1", "image_path": SHOT, "position": "", "outImageName": "",
}, pos={"x": 240, "y": 2190})
node("node_notepad_kill", "App_Kill", "关闭记事本", {
"pid": "${notepadPid}",
}, pos={"x": 240, "y": 2310})
# ---- 板块 D:OCR + AI + 收尾 ----
node("node_ocr", "OcrOffline_GetOcrTextNew", "OCR 识别截图", {
"image_path": SHOT,
}, output_var="ocrText", timeout=120000, pos={"x": 240, "y": 2430})
ai_prompt = (
"这是一次 RPA 自动化的收尾总结。屏幕 OCR 识别到的内容:${ocrText}。"
"数据统计:整理测试文件 ${len(dlFiles)} 个;低价手机 ${len(filteredTable) - 1} 款;"
"最低价 ${minPrice} 元、最高价 ${maxPrice} 元、平均 ${round(avgPrice, 1)} 元。"
"请用中文生成一段 100 字左右的晨间摘要,说明本次自动化完成了哪些事、结果如何,语气轻松一点。"
)
node("node_ai", "DataService_LargeModel", "AI 生成晨间摘要", {
"hint_word": ai_prompt,
}, output_var="aiSummary", timeout=200000, pos={"x": 240, "y": 2550})
node("node_clipboard", "Clipboard_SetText", "摘要复制到剪贴板", {
"text": "${aiSummary}",
}, pos={"x": 240, "y": 2670})
node("node_notice", "Dialog_Notice_V2", "弹窗通知完成", {
"text": "晨间摘要已生成并复制到剪贴板:${aiSummary}", "title": "RPA 晨间助手", "time_out": "20",
}, timeout=60000, pos={"x": 240, "y": 2790})
# ---- 主链连线 ----
main_chain = [
"node_start", "node_scan",
"node_mkdir_zip", "node_mkdir_img", "node_mkdir_doc", "node_mkdir_other",
"node_loop",
"node_read_excel", "node_sort", "node_filter",
"node_min", "node_max", "node_avg", "node_write_cheap",
"node_notepad_run", "node_delay_notepad", "node_notepad_type", "node_shot", "node_notepad_kill",
"node_ocr", "node_ai", "node_clipboard", "node_notice",
]
for a, b in zip(main_chain, main_chain[1:]):
link(a, b)
flow = {
"schemaVersion": 1,
"id": "flow_morning_assistant",
"name": "全家桶 · 晨间自动化助手",
"description": "测试长流程:文件整理(IF分支+遍历+文件操作) → Excel数据加工(读取/排序/筛选/统计/落盘) → 记事本键鼠模拟+截屏 → OCR识别+AI摘要+剪贴板+弹窗通知。",
"enabled": True,
"trigger": {"label": "手动运行", "type": "manual"},
"variables": [],
"nodes": nodes,
"edges": edges,
"meta": {
"createdAt": "2026-11-05T07:25:00.000Z",
"updatedAt": "2026-11-05T07:25:00.000Z",
"appVersion": "0.1.0",
},
}
out = os.path.join(os.environ["APPDATA"], "rpa-studio", "flows", "flow_morning_assistant.json")
with open(out, "w", encoding="utf-8") as f:
json.dump(flow, f, ensure_ascii=False, indent=2)
print("written:", out, "| nodes:", len(nodes), "| edges:", len(edges))