影刀RPA API文档自动生成:从代码到Swagger

影刀RPA API文档自动生成:从代码到Swagger

作者:林焱 | 分类:影刀RPA新手教程 | 难度:★★

什么情况用

开发团队写了API但文档跟不上——代码里的接口改了,文档没同步。导致前端、测试、第三方对接方都在用旧的接口文档,联调时各种404和对不上。

用影刀RPA定时从代码仓库拉取接口定义(如Java的@RestController、Python的FastAPI路由、Go的gin路由),自动生成或更新Swagger文档。代码即文档,代码改了文档自动跟。

怎么做

第一步:确定代码仓库和接口框架

拼多多店群自动化报活动上架!

在配置表中维护需要扫描的项目:


项目名代码仓库框架扫描路径输出Swagger
user-servicegitlab.com/xxx/userSpring Bootsrc/main/java/controller/user-api.yaml
order-apigithub.com/xxx/orderFastAPIapp/routers/order-api.yaml
gatewaygitlab.com/xxx/gwGo-gininternal/handler/gw-api.yaml

第二步:扫描Spring Boot项目注解

# 影刀Python节点:扫描Spring Boot Controllerimportreimportosdefscan_spring_boot_controllers(project_path):"""扫描Java源码中的@RestController注解"""apis=[]forroot,dirs,filesinos.walk(project_path):forfileinfiles:ifnotfile.endswith(".java"):continuefilepath=os.path.join(root,file)withopen(filepath,"r",encoding="utf-8")asf:content=f.read()# 提取@RequestMapping基础路径base_path_match=re.search(r'@RequestMapping\s*\(\s*["\']([^"\']*)["\']',content)base_path=base_path_match.group(1)ifbase_path_matchelse""# 提取所有接口方法methods=re.finditer(r'@(GetMapping|PostMapping|PutMapping|DeleteMapping|RequestMapping)\s*\(\s*(?:value\s*=\s*)?["\']([^"\']*)["\'].*?'r'public\s+\w+\s+(\w+)\s*\((.*?)\)',content,re.DOTALL)forminmethods:http_method=m.group(1).replace("Mapping","").upper()ifhttp_method=="REQUEST":http_method="GET"# 默认GETpath=m.group(2)method_name=m.group(3)params_str=m.group(4)# 提取参数params=[]param_matches=re.finditer(r'@(RequestParam|PathVariable|RequestBody)\s*\(\s*(?:value\s*=\s*)?["\']([^"\']*)["\']',params_str)forpminparam_matches:params.append({"type":pm.group(1),"name":pm.group(2),})full_path=(base_path+path).replace("//","/")apis.append({"method":http_method,"path":full_path,"method_name":method_name,"params":params,"file":os.path.relpath(filepath,project_path),})returnapis apis=scan_spring_boot_controllers("/path/to/project/src")forapiinapis:print(f"{api['method']}{api['path']}{api['method_name']}() [{api['file']}]")

第三步:扫描FastAPI路由

# 影刀Python节点:扫描Python FastAPI路由defscan_fastapi_routers(project_path):"""扫描FastAPI路由定义"""importast apis=[]forroot,dirs,filesinos.walk(project_path):forfileinfiles:ifnotfile.endswith(".py"):continuefilepath=os.path.join(root,file)withopen(filepath,"r",encoding="utf-8")asf:content=f.read()# 查找 @router.get/post/put/delete 或 @app.get/post...patterns=[(r'@(?:router|app)\.(get|post|put|delete|patch)\s*\(\s*["\']([^"\']*)["\']',1,2),]forpattern,method_idx,path_idxinpatterns:matches=re.finditer(pattern,content)forminmatches:apis.append({"method":m.group(method_idx).upper(),"path":m.group(path_idx),"file":os.path.relpath(filepath,project_path),})returnapis

第四步:生成Swagger/OpenAPI文档

# 影刀Python节点:生成OpenAPI 3.0格式defgenerate_openapi(apis,title="API Documentation",version="1.0.0"):"""根据扫描结果生成OpenAPI 3.0 YAML"""yaml_content=f"""openapi: 3.0.0 info: title:{title}version:{version}paths: """# 按path分组fromcollectionsimportdefaultdict paths_grouped=defaultdict(list)forapiinapis:paths_grouped[api["path"]].append(api)forpath,methodsinpaths_grouped.items():yaml_content+=f"{path}:\n"forapiinmethods:yaml_content+=f"{api['method'].lower()}:\n"yaml_content+=f" summary:{api.get('method_name','TBD')}\n"yaml_content+=f" description: Auto-generated from{api.get('file','unknown')}\n"![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9af5a94b0e8e455e9487a3c286f39865.png#pic_center)ifapi.get("params"):yaml_content+=" parameters:\n"forparaminapi["params"]:yaml_content+=f" - name:{param['name']}\n"yaml_content+=f" in:{'path'ifparam['type']=='PathVariable'else'query'}\n"yaml_content+=f" required: true\n"yaml_content+=" schema:\n"yaml_content+=" type: string\n"yaml_content+=" responses:\n"yaml_content+=" '200':\n"yaml_content+=" description: OK\n"returnyaml_content yaml=generate_openapi(apis)withopen("openapi.yaml","w",encoding="utf-8")asf:f.write(yaml)print("OpenAPI文档已生成:openapi.yaml")

第五步:对比差异并更新

# 影刀Python节点:对比新旧文档defdiff_apis(old_apis,new_apis):"""对比两次扫描结果,发现新增/删除/变更的接口"""old_set={(a["method"],a["path"])forainold_apis}new_set={(a["method"],a["path"])forainnew_apis}added=new_set-old_set removed=old_set-new_setreturn{"新增接口":[f"{m}{p}"form,pinadded],"删除接口":[f"{m}{p}"form,pinremoved],"接口总数":len(new_set),"变化数":len(added)+len(removed),}diff=diff_apis(last_scan,current_scan)ifdiff["变化数"]>0:print(f"⚠️ 接口有变化:新增{diff.get('新增接口',0)}个,删除{diff.get('删除接口',0)}个")# 自动更新文档 → 提交到代码仓库 → 通知团队

有什么坑

坑1:注解格式不统一

同一个@GetMapping,有人写@GetMapping("/api/user"),有人写@GetMapping(value = "/api/user"),还有人写@GetMapping(path = "/api/user")。正则表达式需要覆盖多种写法,否则会漏扫。

TEMU店群矩阵自动化运营核价报活动

坑2:动态路由参数无法静态分析

@GetMapping("/api/user/{id}")这种路径参数能分析到,但@GetMapping("/api/user/" + Constants.USER_PATH)这种动态拼接的就分析不到了。这种情况只能手工标注。

坑3:请求体和返回值类型

大多数注解只描述了路径和参数名,没有描述参数类型(int/string)和返回值结构。要获取这些信息需要解析完整的Java/Python类型系统,复杂度高很多。简单版只提取路径和方法名已经很有用了。

坑4:多个项目扫描需要Clone代码

影刀需要先把代码拉到本地才能扫描。如果项目很大(几百MB),Clone本身就很慢。建议用git clone --depth=1只克隆最新版本。

坑5:生成的文档可能不完整

自动生成的Swagger文档只有最基本的路由信息,没有请求示例、错误码说明、业务描述等。需要人工review补充内容,但至少基础框架已经由RPA搭好了。


总结:API文档自动化的第一步是"先有再说"——哪怕生成的文档很简单(只有路径和方法名),也比完全没有文档强。在这个基础上逐步补充描述和示例。