店群自动化项目做到后期,脚本数量会爆炸式增长。
我们统计过:50个店铺、8个平台、20多种任务类型,对应的影刀脚本和Python辅助脚本加起来超过200个。
问题来了:改一个通用函数,怎么知道会影响哪些脚本?上架流程改了,怎么验证所有店铺的上架脚本都正常?新加一个原子操作,怎么确保不会破坏现有的编排流程?
早期我们的做法是:改完脚本,手动跑几个店铺看看。没报错就上线。结果经常出现“开发环境没问题,生产环境挂了”的尴尬。
后来我们花了两个月,搭建了一套脚本CI/CD流水线与自动化测试体系。
这篇文章不讲调度,也不讲运维。专门聊聊如何让店群脚本的开发和发布变得可测试、可回溯、可回滚。
适用场景:店群自动化脚本数量多、迭代频繁的团队。
技术栈:Git + Jenkins/GitLab CI + Docker + 影刀RPA + Pytest。
一、脚本开发的痛点
先说说我们遇到的问题。
痛点一:改动影响面不可知
一个公共的登录函数,被80个上架脚本调用。改了一行代码,不知道哪些脚本会受影响,只能全量回归测试。全量跑一遍需要4小时。
痛点二:环境差异导致“在我这能跑”
开发环境是Windows 10 + 影刀版本6.3,生产环境是Windows Server 2019 + 影刀版本6.5。同一个脚本表现不一样。
痛点三:回滚困难
上线一个新版上架脚本,跑了2小时后发现有个bug,但已经改了几百个商品。想回滚到旧版,但旧版脚本没保留,或者数据库状态已经变了。
痛点四:缺乏回归测试
每次发布都是“信任开发”。没有自动化测试,只能靠人工点几个商品验证。
解决这些问题的核心思路:把脚本当代码管。
二、整体架构:从代码提交到生产发布
我们搭建了一条完整的CI/CD流水线:
阶段一:代码提交
所有影刀脚本(.xaml)和Python辅助脚本(.py)都存储在Git仓库中。每个原子操作独立一个文件,每个店铺的配置也独立。
阶段二:静态检查
- 检查脚本是否符合命名规范
- 检查是否有硬编码的店铺ID或密码
- 检查Python代码的语法和lint问题
阶段三:单元测试(Python)
对Python辅助函数进行单元测试,不依赖浏览器。
阶段四:集成测试(影刀脚本)
在测试环境中,用模拟数据执行影刀脚本,验证关键路径。
阶段五:灰度发布
先在新版脚本上打canary标签,只在1个店铺运行4小时。观察健康度指标。
阶段六:全量发布
灰度通过后,更新stable标签,所有店铺逐步切换。
阶段七:回滚
如果发现严重问题,一键将stable指向上一个版本。
下面详细说每个阶段的工程实现。
三、脚本的版本管理与依赖追踪
我们使用Git管理所有脚本,分支策略:
main:生产环境当前版本develop:集成分支feature/xxx:新功能开发hotfix/xxx:紧急修复
每个脚本文件的头部必须包含元数据注释:
# script: pdd_upload_product_v2.xaml
# version: 2.3.1
# dependencies: login_common, image_processor
# author: linyan
# changelog: 修复了图片上传失败的重试逻辑
CI流程会解析这些元数据,构建依赖图。当login_common脚本变更时,系统自动找出所有依赖它的脚本,并触发这些脚本的回归测试。
依赖图存储在Neo4j或简单的JSON中:
# dependency_graph.py
{
"pdd_upload_product": ["login_common", "image_processor", "price_calculator"],
"temu_upload_product": ["login_common", "image_processor", "translator"],
"batch_price_update": ["login_common", "price_calculator"]
}
这样,改了一个公共模块,CI就知道要跑哪些测试。
四、测试环境隔离:影刀脚本的沙箱
测试环境必须和生产环境隔离,但又要足够真实。
我们搭建了一套测试店铺体系:每个平台都有专用的测试店铺账号,这些店铺不对真实用户开放,可以随意测试。
测试店铺的数据是生产的脱敏子集:商品数量固定、订单数量可控。
测试环境的关键配置:
- 单独的代理IP池(测试专用)
- 单独的Redis和数据库实例
- 影刀RPA使用相同的版本,但配置文件中标记为
env=test - 所有写操作(上架、改价、发货)在测试环境中只写入测试数据,不调用真实支付接口
测试脚本中,我们可以通过环境变量控制行为:
import os
def upload_product(product_data):
env = os.getenv("AUTOMATION_ENV", "production")
if env == "test":
# 测试模式:只验证参数,不实际调用API
return {"success": True, "test_mode": True}
else:
return real_api_call(product_data)
影刀RPA脚本中也可以通过读取外部配置文件来判断环境,比如“如果当前店铺ID以test_开头,则不执行真实发货”。
五、自动化测试框架设计
我们基于Pytest构建了一套测试框架,可以驱动影刀RPA脚本执行并验证结果。
# test_automation.py
import pytest
import subprocess
import json
class YingdaoScriptRunner:
def __init__(self, script_path, shop_id, env="test"):
self.script_path = script_path
self.shop_id = shop_id
self.env = env
def run(self, inputs: dict) -> dict:
"""执行影刀脚本,返回输出结果"""
# 将输入参数写入临时文件
input_file = f"/tmp/input_{self.shop_id}.json"
with open(input_file, 'w') as f:
json.dump(inputs, f)
# 调用影刀命令行执行脚本,传入店铺ID、环境、输入文件路径
cmd = [
"YDBot.exe", "run",
"--script", self.script_path,
"--param", f"shop_id={self.shop_id}",
"--param", f"env={self.env}",
"--param", f"input_file={input_file}",
"--timeout", "120"
]
result = subprocess.run(cmd, capture_output=True, text=True)
# 解析脚本输出的JSON结果
output = json.loads(result.stdout)
return output
def test_pdd_upload_product_success():
runner = YingdaoScriptRunner("scripts/pdd_upload_product.xaml", "test_shop_001")
inputs = {
"product_title": "测试商品-自动化测试",
"price": 99.99,
"image_url": "https://test.com/img.jpg"
}
output = runner.run(inputs)
assert output["code"] == 0
assert output["data"]["product_id"] is not None
def test_pdd_upload_product_duplicate():
# 测试重复上架相同商品应返回错误
runner = YingdaoScriptRunner("scripts/pdd_upload_product.xaml", "test_shop_001")
inputs = {...}
first = runner.run(inputs)
assert first["code"] == 0
second = runner.run(inputs)
assert second["code"] != 0
assert "duplicate" in second["msg"].lower()
对于编排引擎生成的复杂流程,我们测试的是最终业务结果,而非中间步骤。
def test_intelligent_on_off_shelve():
# 执行编排流程
engine = OrchestrationEngine(dsl, {"shop_id": "test_shop_001"})
result = engine.run()
# 验证结果:低库存商品被下架,备选池商品被上架
low_stock_products = get_low_stock_products("test_shop_001", threshold=10)
assert all(p.status == "off_shelf" for p in low_stock_products)
backup_products = get_backup_pool("test_shop_001")
assert len(backup_products) == len(low_stock_products)
测试套件在CI中自动运行,每次提交都会触发。整个测试套件(约200个用例)运行时间控制在15分钟内(并行执行)。
六、灰度发布与流量切换
测试通过后,脚本不是直接全量上线。我们使用标签机制控制哪个店铺用哪个版本的脚本。
配置中心(etcd)存储版本映射:
/scripts/pdd/upload_product/stable -> v2.3.1
/scripts/pdd/upload_product/canary -> v2.4.0-beta
/scripts/temu/upload_product/stable -> v1.8.2
节点管理器在拉取脚本时,根据店铺是否在灰度名单中决定取canary还是stable。
灰度名单动态配置:
# 灰度规则:先跑1个店铺4小时
canary_shops = ["pdd_shop_888"]
灰度期间自动监控对比:
- 灰度店铺 vs 稳定店铺的任务成功率
- 灰度店铺 vs 稳定店铺的平均执行耗时
- 灰度店铺的错误类型分布
如果灰度店铺的指标明显差于稳定店铺,自动回滚(将灰度版本指向稳定版本)。如果没有异常,逐步扩大灰度范围:10%店铺 → 30% → 100%。
整个过程完全自动化,不需要人工干预。
七、一键回滚机制
即使经过了测试和灰度,也可能出现问题。
我们要求每个脚本的发布记录中,必须保留至少最近3个版本的可执行文件。回滚操作只是改变配置中心的stable指针。
但有些脚本修改了数据格式,回滚后新旧数据不兼容怎么办?
解决方案:向前兼容的脚本设计。
所有脚本对输入数据采用宽松读取策略:遇到未知字段忽略,遇到缺失字段使用默认值。这样新版本写的数据,旧版本也能读取(虽然可能处理得不完美,但不会崩溃)。
对于无法兼容的变更(比如数据库表结构调整),我们采用双写+迁移策略:新版本同时写新旧两种格式,运行一周后再废弃旧格式。
八、真实踩坑记录
坑1:影刀脚本无法用命令行传递复杂参数
影刀的命令行参数只支持基础类型。我们通过临时文件传递JSON解决了。
坑2:测试环境与生产环境指纹不同
测试环境的指纹比较简单,导致测试通过的脚本在生产环境因为指纹问题失败。
解决:测试环境也使用和生产环境相同的指纹生成器和代理池,只是店铺账号不同。
坑3:CI运行时间过长
全量回归测试需要4小时,严重拖慢发布节奏。
解决:只运行受影响的脚本测试。通过依赖图,只测试变更脚本及其依赖链上的脚本。平均耗时降到20分钟。
坑4:测试数据污染
多个测试并行运行时,共用了同一个测试店铺,导致状态互相干扰。
解决:每个测试用例运行前,重置测试店铺的环境(清理购物车、删除测试商品、恢复默认配置)。同时支持并行测试时自动分配独立的测试子账号。
九、度量与改进
引入CI/CD体系后,我们跟踪了几个关键指标:
- 脚本发布频率:从每周2次提升到每天5次
- 生产环境故障率:降低了80%(因为大部分问题在测试和灰度阶段发现)
- 回滚平均时间:从30分钟(手动找旧版本)降到1分钟(改配置)
- 新人加入成本:从2周降到3天(因为有了完善的测试用例作为参考)
我们还做了一个“脚本健康度”仪表盘,展示每个脚本的最近发布版本、测试覆盖率、最近一次测试结果、灰度状态。
十、总结:脚本也是软件
很多做RPA的人,把脚本当成一次性工具,写完能跑就行。
但在店群规模下,脚本的生命周期维护成本远大于开发成本。没有CI/CD,没有自动化测试,没有灰度发布,迟早会被脚本管理压垮。
影刀RPA本身不提供这些能力,但我们可以用外部的工程实践来补全。
投入产出比很高:花两周搭好框架,后续每个月的维护时间节省80%以上。
如果你有10个以上活跃脚本,建议开始考虑引入版本管理、自动化测试和灰度发布。这不是过度工程,而是规模化运营的必经之路。
作者:林焱
