完善模板,加入RPA标准
This commit is contained in:
115
references/ADAPTER.md
Normal file
115
references/ADAPTER.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# 适配器标准:真实/仿真 × API/RPA 四档模式
|
||||
|
||||
> 凡是"连接三方系统"(ERP、CRM、SaaS、银行等)的 skill,都应采用本文的 **adapter 四档模式**。这样同一套业务逻辑可以在不同档位间切换:开发用仿真、CI 用 mock、上线用真实,互不影响。
|
||||
|
||||
## 为什么要分档
|
||||
|
||||
对接一个外部系统,工程上无非四种方式的组合:
|
||||
|
||||
| | API(有接口) | RPA(操作界面) |
|
||||
|---|---|---|
|
||||
| **真实系统** | `real_api` 真实 API | `real_rpa` 真实界面 |
|
||||
| **仿真系统** | `mock` 离线仿真 | `sim_rpa` 仿真平台界面 |
|
||||
|
||||
- **mock**:纯离线、不联网,给单测 / CI / 开发自测,**保证可重复**。
|
||||
- **sim_rpa**:操作仿真平台(如 `sandbox.jc2009.com`),跑真实端到端流程但不碰生产,**默认开发档位**。
|
||||
- **real_api**:有官方接口时**首选**(最稳、最快、最易维护)。
|
||||
- **real_rpa**:没有 API 只能操作生产界面,**风险最高、放最后**。
|
||||
|
||||
> 推荐优先级:**real_api > sim_rpa > real_rpa**,mock 永远保留做 CI。
|
||||
|
||||
## 目录骨架
|
||||
|
||||
业务逻辑只依赖 `base` 里定义的接口与数据契约,具体档位实现各自一个文件:
|
||||
|
||||
```
|
||||
scripts/service/<domain>_adapter/
|
||||
__init__.py # dispatch:按档位返回对应 adapter 实例
|
||||
base.py # 数据契约(dataclass)+ AdapterBase 接口(NotImplementedError)
|
||||
mock.py # 离线仿真,给 CI/单测
|
||||
real_api.py # 真实系统 API
|
||||
sim_rpa.py # 仿真平台 RPA(Playwright/pywinauto/uiautomator2)
|
||||
real_rpa.py # 真实系统 RPA(占位,谨慎实现)
|
||||
```
|
||||
|
||||
> 参考实现:`disburse-payroll-icbc/scripts/service/disburse_adapter/`(已有 `base.py` / `mock.py` / `icbc_simulator_rpa.py`)。
|
||||
> 模板示例:`scripts/service/example_adapter/`(复制改名即用)。
|
||||
|
||||
### 引用方式
|
||||
|
||||
```python
|
||||
from service.example_adapter import get_adapter
|
||||
from service.example_adapter.base import ExampleItem
|
||||
|
||||
adapter = get_adapter() # 由 OPENCLAW_TEST_TARGET 决定档位
|
||||
result = adapter.do_batch(
|
||||
target="https://sandbox.jc2009.com",
|
||||
items=[ExampleItem(id="1", label="demo")],
|
||||
)
|
||||
```
|
||||
|
||||
mock 档位纯离线验证:
|
||||
|
||||
```bash
|
||||
set OPENCLAW_TEST_TARGET=mock
|
||||
python -c "import sys; sys.path.insert(0,'scripts'); from service.example_adapter import get_adapter; from service.example_adapter.base import ExampleItem; r=get_adapter().do_batch('x',[ExampleItem('1','a')]); print(r.ok, r.serial_no)"
|
||||
```
|
||||
|
||||
## base.py 写法(要点)
|
||||
|
||||
1. **数据契约用 `@dataclass`**:输入项(如 `PayrollItem`)、批量结果(如 `BatchResult{ok, serial_no, error_msg, artifacts}`)。
|
||||
2. **接口方法抛 `NotImplementedError`**:所有档位实现同一签名。
|
||||
3. **以"批"为单位返回**:真实 RPA 往往是整批提交,结果按批次返回,不要假设逐条。
|
||||
4. **结果里带 `artifacts`**:截图/录屏/日志路径,便于排查。
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class XxxResult:
|
||||
ok: bool
|
||||
serial_no: Optional[str]
|
||||
error_msg: Optional[str]
|
||||
artifacts: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
class AdapterBase:
|
||||
name = "base"
|
||||
def do_batch(self, target: str, items: list) -> XxxResult:
|
||||
raise NotImplementedError
|
||||
```
|
||||
|
||||
## 档位 dispatch
|
||||
|
||||
由 `OPENCLAW_TEST_TARGET` 统一决定用哪个 adapter(与现有测试体系一致):
|
||||
|
||||
| `OPENCLAW_TEST_TARGET` | adapter | 用途 |
|
||||
|---|---|---|
|
||||
| `unit` / `mock` | `MockAdapter` | 单测 / CI,离线 |
|
||||
| `simulator_rpa`(默认) | `SimRpaAdapter` | 开发/演示,操作仿真平台 |
|
||||
| `real_api` | `RealApiAdapter` | 生产,走官方接口 |
|
||||
| `real_rpa` | `RealRpaAdapter` | 生产,操作真实界面(最后手段) |
|
||||
|
||||
```python
|
||||
# __init__.py
|
||||
def get_adapter():
|
||||
target = (os.environ.get("OPENCLAW_TEST_TARGET") or "simulator_rpa").lower()
|
||||
if target in ("unit", "mock"):
|
||||
return MockAdapter()
|
||||
if target == "real_api":
|
||||
return RealApiAdapter()
|
||||
if target == "real_rpa":
|
||||
return RealRpaAdapter()
|
||||
return SimRpaAdapter() # 默认
|
||||
```
|
||||
|
||||
> 实际可改为读 `.env` 里的运行模式(见 `CONFIG.md`),`OPENCLAW_TEST_TARGET` 作为进程级覆盖优先。
|
||||
|
||||
## 测试要求
|
||||
|
||||
- **mock 档必须能纯离线跑通**,进 CI。
|
||||
- **每个 adapter 单测同一组用例**(契约测试),保证档位间行为一致。
|
||||
- RPA 档的真实联网测试单独标记,不进默认 CI(参考 `tests/desktop/`)。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- `RPA.md` — 三端 RPA 技术选型与拟人/反反爬范式
|
||||
- `CONFIG.md` — `.env` 里如何配置运行模式与目标地址
|
||||
- `TESTING.md` — 测试 target 与隔离体系
|
||||
Reference in New Issue
Block a user