影刀RPA 配置中心设计:流程参数集中管理

影刀RPA 配置中心设计:流程参数集中管理

署名:林焱

什么情况用

你的RPA流程里有20个参数:数据库地址、账号密码、网页URL、超时时间、文件路径……散落在各个组件里。改一个数据库地址,要翻遍整个流程找哪里用了旧地址。更惨的是,5个流程共用同一套配置,改了一个忘了改其他四个。

配置中心就是把这些参数集中到一个地方管理——改一处,所有流程生效。就像家里的电箱,所有开关集中在一个面板上,不用满屋子找开关。

典型场景:

  • 多个RPA流程共用同一套数据库配置

  • 开发/测试/生产环境切换(不同数据库、不同URL)

  • 密码等敏感信息统一管理,不散落在代码里

  • 非开发人员需要修改参数(如采集频率、收件人邮箱)

怎么做

1. JSON配置文件

店群矩阵自动化突破运营极限!

最简单的配置中心——一个JSON文件搞定:

importjsonimportos# 创建配置文件config={"database":{"host":"192.168.1.100","port":3306,"user":"rpa_user","password":"RPA@2024#secure","database":"erp_system"},"web":{"target_url":"https://www.example.com","timeout":30,"retry_times":3,"headless":False},"excel":{"output_dir":"C:\\RPAData\\导出","template_dir":"C:\\RPAData\\模板","max_rows_per_sheet":50000},![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6d2207e37fd84feebfcf843b131cfdcf.png#pic_center)"email":{"smtp_server":"smtp.qq.com","smtp_port":465,"sender":"rpa_bot@qq.com","password":"email_auth_code","recipients":["manager@company.com","boss@company.com"]},"schedule":{"crawl_interval_minutes":60,"report_send_hour":9,"cleanup_days":30},"environment":"production"}config_path=r"C:\RPAData\config.json"withopen(config_path,'w',encoding='utf-8')asf:json.dump(config,f,indent=2,ensure_ascii=False)print(f"配置文件已创建:{config_path}")

2. 配置读取封装

importjsonimportosfromfunctoolsimportlru_cacheclassConfigManager:"""配置管理器"""def__init__(self,config_path=None):ifconfig_pathisNone:# 默认配置文件路径config_path=os.path.join(os.environ.get('USERPROFILE',''),'RPAData','config.json')self.config_path=config_path self._config=Nonedefload(self):"""加载配置"""ifnotos.path.exists(self.config_path):raiseFileNotFoundError(f"配置文件不存在:{self.config_path}")withopen(self.config_path,'r',encoding='utf-8')asf:self._config=json.load(f)returnself._configdefget(self,key_path,default=None):""" 用点号路径获取配置值 如:config.get('database.host') """ifself._configisNone:self.load()keys=key_path.split('.')value=self._configforkeyinkeys:ifisinstance(value,dict)andkeyinvalue:value=value[key]else:returndefaultreturnvaluedefset(self,key_path,value):"""设置配置值"""ifself._configisNone:self.load()keys=key_path.split('.')config=self._configforkeyinkeys[:-1]:ifkeynotinconfig:config[key]={}config=config[key]config[keys[-1]]=valuedefsave(self):"""保存配置"""withopen(self.config_path,'w',encoding='utf-8')asf:json.dump(self._config,f,indent=2,ensure_ascii=False)defreload(self):"""重新加载配置"""self._config=Nonereturnself.load()# 使用示例config=ConfigManager(r"C:\RPAData\config.json")# 读取配置db_host=config.get('database.host')db_port=config.get('database.port')target_url=config.get('web.target_url')recipients=config.get('email.recipients',[])print(f"数据库:{db_host}:{db_port}")print(f"目标URL:{target_url}")print(f"收件人:{recipients}")# 修改配置config.set('web.timeout',60)config.set('email.recipients',['new_manager@company.com'])config.save()print("配置已更新")

3. 多环境配置

importjsonimportosclassEnvironmentConfig:"""多环境配置管理"""def__init__(self,config_dir=None):self.config_dir=config_dirorr"C:\RPAData\config"self.env=self._detect_environment()self.config=self._load_config()def_detect_environment(self):"""自动检测当前环境"""# 方式1:环境变量env=os.environ.get('RPA_ENV','')ifenv:returnenv# 方式2:机器名判断hostname=os.environ.get('COMPUTERNAME','').lower()if'prod'inhostnameor'server'inhostname:return'production'elif'test'inhostnameor'dev'inhostname:return'test'else:return'development'def_load_config(self):"""加载当前环境的配置"""# 先加载基础配置base_path=os.path.join(self.config_dir,'base.json')withopen(base_path,'r',encoding='utf-8')asf:config=json.load(f)# 再加载环境特定配置(覆盖基础配置)env_path=os.path.join(self.config_dir,f'{self.env}.json')ifos.path.exists(env_path):withopen(env_path,'r',encoding='utf-8')asf:env_config=json.load(f)config=self._deep_merge(config,env_config)config['_environment']=self.envreturnconfigdef_deep_merge(self,base,override):"""深度合并两个字典"""result=base.copy()forkey,valueinoverride.items():ifkeyinresultandisinstance(result[key],dict)andisinstance(value,dict):result[key]=self._deep_merge(result[key],value)else:result[key]=valuereturnresultdefget(self,key_path,default=None):keys=key_path.split('.')value=self.configforkeyinkeys:ifisinstance(value,dict)andkeyinvalue:value=value[key]else:returndefaultreturnvalue# 配置文件结构:# C:\RPAData\config\# ├── base.json # 基础配置(所有环境共用)# ├── development.json # 开发环境覆盖配置# ├── test.json # 测试环境覆盖配置# └── production.json # 生产环境覆盖配置# base.json# {# "app_name": "RPA采集系统",# "version": "2.0",# "database": {"port": 3306, "charset": "utf8mb4"},# "web": {"timeout": 30, "retry": 3}# }# development.json# {# "database": {"host": "localhost", "user": "dev", "password": "dev123"},# "web": {"target_url": "https://dev.example.com"}# }# production.json# {# "database": {"host": "192.168.1.100", "user": "rpa_prod", "password": "Prod@2024"},# "web": {"target_url": "https://www.example.com", "timeout": 60}# }# 使用env_config=EnvironmentConfig(r"C:\RPAData\config")print(f"当前环境:{env_config.get('_environment')}")print(f"数据库地址:{env_config.get('database.host')}")print(f"目标URL:{env_config.get('web.target_url')}")

4. 配置热更新

importjsonimportosimporttimeclassHotReloadConfig:"""支持热更新的配置管理器"""def__init__(self,config_path):self.config_path=config_path self._config=Noneself._last_mtime=0self._load()def_load(self):"""加载配置并记录修改时间"""withopen(self.config_path,'r',encoding='utf-8')asf:self._config=json.load(f)self._last_mtime=os.path.getmtime(self.config_path)def_check_reload(self):"""检查配置文件是否被修改,是则重新加载"""try:current_mtime=os.path.getmtime(self.config_path)ifcurrent_mtime>self._last_mtime:print("检测到配置文件变更,重新加载...")self._load()exceptFileNotFoundError:passdefget(self,key_path,default=None):"""每次获取值时检查是否需要重新加载"""self._check_reload()keys=key_path.split('.')value=self._configforkeyinkeys:ifisinstance(value,dict)andkeyinvalue:value=value[key]else:returndefaultreturnvalue# 使用:修改config.json后,不需要重启RPA流程,下次get自动读取最新值config=HotReloadConfig(r"C:\RPAData\config.json")# 模拟流程持续运行foriinrange(100):timeout=config.get('web.timeout',30)# 如果config.json被修改,这里自动获取新值url=config.get('web.target_url')print(f"[{i}] URL={url}, timeout={timeout}")time.sleep(10)

5. 配置验证

importjsonimportosdefvalidate_config(config):"""验证配置完整性"""errors=[]warnings=[]# 检查必填项required_keys=[('database.host','数据库地址'),('database.port','数据库端口'),('database.user','数据库用户名'),('database.password','数据库密码'),('web.target_url','目标URL'),('email.smtp_server','邮件服务器'),]forkey_path,descinrequired_keys:value=_get_nested(config,key_path)ifvalueisNoneorvalue=='':errors.append(f"缺少必填配置:{desc}{key_path})")# 检查类型ifisinstance(_get_nested(config,'database.port'),str):warnings.append("database.port 应为整数,当前为字符串")# 检查URL格式url=_get_nested(config,'web.target_url')ifurlandnoturl.startswith(('http://','https://')):errors.append(f"web.target_url 格式错误:{url}")# 检查路径是否存在output_dir=_get_nested(config,'excel.output_dir')ifoutput_dirandnotos.path.exists(output_dir):warnings.append(f"输出目录不存在:{output_dir}(将自动创建)")# 检查密码强度password=_get_nested(config,'database.password')ifpasswordandlen(password)<8:warnings.append("数据库密码长度不足8位,建议加强")returnerrors,warningsdef_get_nested(d,key_path):keys=key_path.split('.')value=dforkeyinkeys:ifisinstance(value,dict)andkeyinvalue:value=value[key]else:returnNonereturnvalue# 使用config_path=r"C:\RPAData\config.json"withopen(config_path,'r',encoding='utf-8')asf:config=json.load(f)errors,warnings=validate_config(config)iferrors:print("❌ 配置错误:")foreinerrors:print(f" -{e}")raiseValueError("配置校验失败,请修正后重试")ifwarnings:print("⚠ 配置警告:")forwinwarnings:print(f" -{w}")print("✓ 配置校验通过")

有什么坑

坑1:密码明文存储在配置文件中

config.json里的密码是明文,任何能访问文件的人都能看到。生产环境中必须加密:

importosfromcryptography.fernetimportFernet# 生成密钥(只需一次,妥善保管)key=Fernet.generate_key()# 把key保存到安全位置(如Windows凭据管理器)# 加密密码cipher=Fernet(key)encrypted_password=cipher.encrypt(b"RPA@2024#secure")# 把encrypted_password存入配置文件# 解密decrypted=cipher.decrypt(encrypted_password)

或者更简单:用环境变量存密码,配置文件只存非敏感信息:

importos# 配置文件里不存密码# config.json: {"database": {"host": "...", "user": "..."}}# 密码从环境变量读取db_password=os.environ.get('RPA_DB_PASSWORD')ifnotdb_password:raiseEnvironmentError("请设置环境变量 RPA_DB_PASSWORD")

坑2:配置文件被多人同时修改

temu店群自动化报活动案例

A改了timeout从30到60,B同时改了retry从3到5,A先保存,B后保存——B的保存覆盖了A的修改,timeout又变回30。

简单解决:配置文件只允许一个人改,或者用数据库存储配置(数据库有行锁)。如果必须用文件,加文件锁:

importfcntl# Linux only# Windows用msvcrt或pywin32的LockFile# 或简单地用"配置修改记录"表# 每次修改前先读取最新版本号,修改后版本号+1# 如果版本号不匹配,说明有人抢先修改了,需要重新读取

坑3:配置项过多导致JSON文件巨大

配置项从20个增长到200个,JSON文件几百行,找一项要翻半天。解决:按模块拆分:

C:\RPAData\config\ ├── database.json # 数据库相关 ├── web.json # 网页相关 ├── excel.json # Excel相关 ├── email.json # 邮件相关 └── schedule.json # 调度相关
classMultiFileConfig:def__init__(self,config_dir):self.config_dir=config_dir self._cache={}defget(self,module,key,default=None):"""如:config.get('database', 'host')"""ifmodulenotinself._cache:path=os.path.join(self.config_dir,f'{module}.json')ifos.path.exists(path):withopen(path,'r',encoding='utf-8')asf:self._cache[module]=json.load(f)else:returndefaultreturnself._cache[module].get(key,default)

坑4:配置文件路径写死

配置文件路径硬编码为C:\RPAData\config.json,换台电脑就找不到。解决:用相对路径或环境变量:

# 方案1:环境变量指定配置文件位置config_path=os.environ.get('RPA_CONFIG_PATH',os.path.join(os.path.dirname(__file__),'config.json'))# 方案2:从用户目录读取config_path=os.path.join(os.path.expanduser('~'),'.rpa','config.json')# 方案3:影刀全局变量# 在影刀客户端设置中配置全局变量CONFIG_PATH,流程中用【获取全局变量】读取

坑5:缺少配置导致流程中途崩溃

流程跑到第50步,才发现某个配置项没设置,直接报错退出。前面50步白跑了。

# 解决方案:流程启动时先做配置完整性检查config=ConfigManager(config_path).load()errors,warnings=validate_config(config)iferrors:# 配置不完整,不启动流程print("配置不完整,无法启动:")![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/bf1c25a3d63349b9ac77c56c169bbeb2.png#pic_center)foreinerrors:print(f"{e}")# 发送通知给管理员send_alert("RPA流程启动失败",f"配置错误:\n"+"\n".join(errors))sys.exit(1)