规范化后的skill
This commit is contained in:
+112
-194
@@ -1,257 +1,175 @@
|
||||
# E10 内部 API 参考
|
||||
# E10 Internal Read-Only APIs
|
||||
|
||||
这些是 E10 管理后台前端调用的内部 REST API(非 OpenAPI),在浏览器已登录的情况下可直接用 `fetch()` 调用,session cookie 自动携带。适合替代复杂的 UI 点击操作获取数据。
|
||||
These APIs are E10 management UI backend APIs, not external OpenAPI endpoints. Use them only inside an authenticated browser session to read metadata needed for documentation. Do not expose these paths, request bodies, response fields, database table names, or internal IDs in the final external-facing documentation.
|
||||
|
||||
> **适用场景**:登录后(Phase 1),所有流程发现、表单查询、字段提取均可通过这些 API 完成,无需逐层点击 UI。
|
||||
## Use cases
|
||||
|
||||
---
|
||||
Use these APIs after login to avoid fragile UI navigation when discovering workflow fields:
|
||||
|
||||
## 1. 获取所有工作流列表
|
||||
1. Get workflow list and target `workflowId`.
|
||||
2. Search form list and target `formId`.
|
||||
3. Read form metadata and detect detail tables.
|
||||
4. Read main-table and detail-table field definitions.
|
||||
|
||||
**用途**:按分类树形列出所有工作流,找到目标流程的 `workflowId`(也叫 pathSetId)。
|
||||
All examples use `fetch()` inside the browser session, so the authenticated session cookie is included automatically.
|
||||
|
||||
```
|
||||
## 1. Get workflow list
|
||||
|
||||
Endpoint:
|
||||
|
||||
```text
|
||||
POST /api/bs/workflow/pathdef/baseSet/getBaseInfoListTree
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"searchParams": {
|
||||
"otherSearchDatas": {
|
||||
"workflowType": "",
|
||||
"workflowId": "",
|
||||
"subCompanyId": ""
|
||||
}
|
||||
},
|
||||
"isTemplate": 0,
|
||||
"belongType": 1
|
||||
}
|
||||
```
|
||||
|
||||
**响应结构**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"total": 42,
|
||||
"dataGroupTitle": [
|
||||
{"typeName": "行政类", "count": 9, "id": "1184726218683441153"}
|
||||
],
|
||||
"datas": [
|
||||
{
|
||||
"typeName": "行政类",
|
||||
"children": [
|
||||
{
|
||||
"id": "1185512781721886720",
|
||||
"title": "06.用车审批流程",
|
||||
"defaultWorkflowName": "06.用车审批流程",
|
||||
"status": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"searchParams": {
|
||||
"otherSearchDatas": {
|
||||
"workflowType": "",
|
||||
"workflowId": "",
|
||||
"subCompanyId": ""
|
||||
}
|
||||
},
|
||||
"isTemplate": 0,
|
||||
"belongType": 1
|
||||
}
|
||||
```
|
||||
|
||||
**关键字段**:
|
||||
- `datas[].children[].id` — workflowId(pathSetId),用于后续查询
|
||||
- `datas[].children[].title` — 流程名称
|
||||
- `datas[].typeName` — 流程分类(人事类/行政类/财务类/业务类)
|
||||
Important response fields:
|
||||
|
||||
---
|
||||
- `data.datas[].typeName`: workflow category.
|
||||
- `data.datas[].children[].id`: workflowId/pathSetId.
|
||||
- `data.datas[].children[].title`: workflow display name.
|
||||
|
||||
## 2. 搜索表单(按名称)
|
||||
|
||||
**用途**:根据表单名称搜索,获取 `formId`。名称支持模糊匹配。
|
||||
Browser snippet:
|
||||
|
||||
```python
|
||||
import json
|
||||
result = json.loads(js('''
|
||||
(async () => {
|
||||
let resp = await fetch("/api/bs/workflow/pathdef/baseSet/getBaseInfoListTree", {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({
|
||||
searchParams: { otherSearchDatas: { workflowType: "", workflowId: "", subCompanyId: "" } },
|
||||
isTemplate: 0,
|
||||
belongType: 1
|
||||
})
|
||||
});
|
||||
return JSON.stringify(await resp.json());
|
||||
})();
|
||||
'''))
|
||||
```
|
||||
|
||||
## 2. Search forms
|
||||
|
||||
Endpoint:
|
||||
|
||||
```text
|
||||
POST /api/workflow/core/form/formmanage/getFormList
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"module": "workflow",
|
||||
"pageNo": 1,
|
||||
"pageSize": 50,
|
||||
"name": "用车"
|
||||
"module": "workflow",
|
||||
"pageNo": 1,
|
||||
"pageSize": 50,
|
||||
"name": "<CORE_WORKFLOW_KEYWORD>"
|
||||
}
|
||||
```
|
||||
|
||||
**响应结构**:
|
||||
Important response fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"pageDatas": {
|
||||
"result": [
|
||||
{
|
||||
"id": "1185512764483297325",
|
||||
"name": "用车审批流程"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- `data.pageDatas.result[].id`: formId.
|
||||
- `data.pageDatas.result[].name`: form name.
|
||||
|
||||
**关键字段**:
|
||||
- `data.pageDatas.result[].id` — formId
|
||||
- `data.pageDatas.result[].name` — 表单名称
|
||||
Workflow names may include numbering or prefixes, while form names often do not. Search by core business keyword rather than exact full workflow title.
|
||||
|
||||
> **注意**:流程名称(如"06.用车审批流程")与表单名称(如"用车审批流程")可能不同(有无编号前缀)。搜索时去掉编号前缀。
|
||||
## 3. Get form metadata
|
||||
|
||||
---
|
||||
Endpoint:
|
||||
|
||||
## 3. 获取表单详情
|
||||
|
||||
**用途**:获取表单的元数据(表名、类型、基础设置)。
|
||||
|
||||
```
|
||||
```text
|
||||
POST /api/workflow/core/form/formmanage/getForm
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"module": "workflow",
|
||||
"form": {
|
||||
"id": "1185512764483297325"
|
||||
}
|
||||
"module": "workflow",
|
||||
"form": {
|
||||
"id": "<FORM_ID>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应结构**:
|
||||
Important response fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"id": "1185512764483297325",
|
||||
"tableName": "ft_ycsplc",
|
||||
"name": "用车审批流程",
|
||||
"tableType": "MAIN",
|
||||
"systemTable": false,
|
||||
"editTable": true
|
||||
}
|
||||
}
|
||||
```
|
||||
- `data.name`: form name.
|
||||
- `data.tableType`: form table type.
|
||||
- `data.detailTable`: detail table metadata when available.
|
||||
- `data.tableName`: internal database table name. Use only for internal reasoning and never include it in final external documentation.
|
||||
|
||||
**关键字段**:
|
||||
- `data.tableName` — 数据库表名
|
||||
- `data.tableType` — MAIN(主表)或 DETAIL(明细表)
|
||||
- `data.name` — 表单名称
|
||||
## 4. Get field definitions
|
||||
|
||||
---
|
||||
Endpoint:
|
||||
|
||||
## 4. 获取表单字段列表(分页)
|
||||
|
||||
**用途**:获取主表或明细表的所有字段定义(含 fieldId、dataKey、类型、分组)。
|
||||
|
||||
```
|
||||
```text
|
||||
POST /api/workflow/core/form/field/manage/getFormFieldPage
|
||||
```
|
||||
|
||||
**请求体(主表字段)**:
|
||||
Request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNo": 1,
|
||||
"pageSize": 20,
|
||||
"module": "workflow",
|
||||
"formFieldSearchEntity": {
|
||||
"isDelete": 0,
|
||||
"status": "enable",
|
||||
"formId": "1185512764483297325",
|
||||
"formTableId": "1185512764483297435"
|
||||
}
|
||||
"pageNo": 1,
|
||||
"pageSize": 100,
|
||||
"module": "workflow",
|
||||
"formFieldSearchEntity": {
|
||||
"isDelete": 0,
|
||||
"status": "enable",
|
||||
"formId": "<FORM_ID>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**请求体(明细表字段)**:同上,`formTableId` 替换为明细表的 table ID。
|
||||
If needed, add `formTableId` to target a specific main or detail table. If passing `formTableId` returns zero records for the main table, retry without `formTableId`.
|
||||
|
||||
**响应结构**:
|
||||
Important response fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"pageDatas": {
|
||||
"pageNo": 1,
|
||||
"pageSize": 20,
|
||||
"result": [
|
||||
{
|
||||
"title": "申请人",
|
||||
"dataKey": "sqr",
|
||||
"type": "Employee",
|
||||
"fieldId": "1185512764483297398",
|
||||
"showOrder": 0,
|
||||
"groupName": "基础信息",
|
||||
"columnName": "sqr",
|
||||
"formTableId": "1185512764483297435",
|
||||
"formId": "1185512764483297325"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- `title`: business field display name.
|
||||
- `dataKey`: request payload field key.
|
||||
- `fieldId`: internal field ID. Usually not exposed unless the OpenAPI payload explicitly requires it.
|
||||
- `type`: E10 component/API field type.
|
||||
- `showOrder`: display order.
|
||||
- `groupName`: internal UI group. Use only to infer business grouping; do not copy it blindly.
|
||||
- `columnName`: internal database column. Never include in external documentation.
|
||||
- `formTableId`: table ID, useful for follow-up field queries.
|
||||
|
||||
**关键字段**:
|
||||
- `title` — 字段显示名称
|
||||
- `dataKey` — 字段编码(API 传参用)
|
||||
- `fieldId` — 字段 ID(API 传参用,纯数字)
|
||||
- `type` — 字段类型(Employee/Date/Text/TextArea/Department/SubCompany/File/EBuilder/Flow/Document)
|
||||
- `showOrder` — 显示顺序
|
||||
- `groupName` — 所属分组
|
||||
- `columnName` — 数据库列名
|
||||
## Type mapping for external documentation
|
||||
|
||||
**常见字段类型映射**:
|
||||
| E10 type | External description | Common payload guidance |
|
||||
|---|---|---|
|
||||
| Employee | Personnel field | Use `dataOptions`, `type: "resource"`, and `userType` such as `JOB_NUM`, `EMAIL`, `MOBILE`, `idNos`, `loginID`, or `account`. |
|
||||
| Department | Department field | Use `dataOptions`, `type: "department"`, and `deptType` such as `DEPT_CODE` or `DEPT_NAME`. |
|
||||
| SubCompany | Sub-company field | Use `dataOptions`, `type: "subcompany"`; prefer an external organization code when supported. |
|
||||
| Date | Date | Use ISO-like date string such as `2026-05-27`. |
|
||||
| Text | Single-line text | Use `content` string. |
|
||||
| TextArea | Multi-line text | Use `content` string. |
|
||||
| File | Attachment | Upload file first, then reference upload result in `dataOptions`. |
|
||||
| EBuilder | Related e-builder object | Use documented option/object mapping. Mark as needing confirmation if not clear. |
|
||||
| Flow | Related workflow | Use documented option/object mapping. Mark as needing confirmation if not clear. |
|
||||
| Document | Related document | Use documented option/object mapping. Mark as needing confirmation if not clear. |
|
||||
|
||||
| API type | 中文含义 | formData 传值方式 |
|
||||
|----------|----------|-------------------|
|
||||
| Employee | 人员选择 | `dataOptions[].type: "resource"` |
|
||||
| Department | 部门选择 | `dataOptions[].type: "department"` |
|
||||
| SubCompany | 分部选择 | `dataOptions[].type: "subcompany"` |
|
||||
| Date | 日期 | `content: "2026-05-27"` |
|
||||
| Text | 单行文本 | `content: "文本值"` |
|
||||
| TextArea | 多行文本 | `content: "文本值"` |
|
||||
| File | 附件 | `dataOptions[].type: "file"` + uploadParam |
|
||||
| EBuilder | 关联e-builder | 选项型传值 |
|
||||
| Flow | 关联流程 | 选项型传值 |
|
||||
| Document | 关联文档 | 选项型传值 |
|
||||
## Alternative formId discovery
|
||||
|
||||
---
|
||||
If `getFormList` cannot identify the form, use these fallback methods:
|
||||
|
||||
## 5. 获取 formTableId(主表和明细表)
|
||||
|
||||
formTableId 可以从以下途径获取:
|
||||
|
||||
1. **XHR 拦截**:在字段管理页面切换 主表/明细表 标签时,页面会调用 `getFormFieldPage`,其请求体中的 `formTableId` 即为当前表的 ID。
|
||||
2. **字段响应反推**:主表任一字段的 `formTableId` 即为主表的 table ID。
|
||||
3. **明细表为空时**:不需要 formTableId。
|
||||
|
||||
---
|
||||
|
||||
## formId 的其他获取方式
|
||||
|
||||
除了 `getFormList` API 搜索,还可从以下途径获取 formId:
|
||||
|
||||
1. **localStorage**:访问过表单管理页面后,`localStorage` 中会有 key `Form_0_<formId>_checkNewForm`,从中提取数字部分。
|
||||
|
||||
```js
|
||||
for (var key in localStorage) {
|
||||
if (key.startsWith("Form_0_") && key.endsWith("_checkNewForm")) {
|
||||
return key.replace("Form_0_", "").replace("_checkNewForm", "");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **URL 路径**(不可靠,仅作参考):流程详情页 URL 中的 `pathset/` 后数字是 workflowId,不是 formId。
|
||||
1. After visiting form management UI, inspect localStorage keys matching `Form_0_<formId>_checkNewForm`.
|
||||
2. Intercept XHR requests to `getFormFieldPage` after switching field tabs and inspect the request body.
|
||||
3. Do not treat `pathset/<id>` in the workflow detail URL as formId; that value is workflowId/pathSetId.
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
# Login and Browser Harness
|
||||
|
||||
Use this reference only when live E10 access is required.
|
||||
|
||||
## Safety requirements
|
||||
|
||||
- Use an isolated Chrome instance, never the user's daily browser profile.
|
||||
- Do not store credentials in files, memory, shell history notes, examples, or final documentation.
|
||||
- Keep all E10 activity read-only.
|
||||
- Clean up the isolated browser process when finished.
|
||||
|
||||
## Cross-platform placeholders
|
||||
|
||||
| Placeholder | Linux | macOS | Windows |
|
||||
|---|---|---|---|
|
||||
| `<CHROME>` | `google-chrome` | `/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome` | `"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"` |
|
||||
| `<TEMP_DIR>` | `/tmp/bh-e10` | `/tmp/bh-e10` | `%TEMP%\\bh-e10` |
|
||||
| `<SCREENSHOT_DIR>` | `/tmp` | `/tmp` | `%TEMP%` |
|
||||
|
||||
## Start isolated Chrome
|
||||
|
||||
Clean stale browser-harness daemons first. Then start Chrome with a dedicated remote debugging port and profile directory.
|
||||
|
||||
Linux/macOS pattern:
|
||||
|
||||
```bash
|
||||
pkill -f browser_harness.daemon 2>/dev/null; rm -f /tmp/bu-*.sock /tmp/bu-*.pid 2>/dev/null
|
||||
<CHROME> --headless=new --remote-debugging-port=9223 --user-data-dir=<TEMP_DIR> --no-first-run --no-default-browser-check --window-size=1920,1080
|
||||
sleep 3 && curl -s http://127.0.0.1:9223/json/version | grep webSocketDebuggerUrl
|
||||
```
|
||||
|
||||
Every browser-harness command must set `BU_CDP_URL` in the same shell command:
|
||||
|
||||
```bash
|
||||
export BU_CDP_URL=http://127.0.0.1:9223 && browser-harness <<'PY'
|
||||
# commands
|
||||
PY
|
||||
```
|
||||
|
||||
Do not run `export BU_CDP_URL=...` on a separate line and then invoke `browser-harness`; the daemon may not inherit the later shell environment.
|
||||
|
||||
Windows pattern:
|
||||
|
||||
```bat
|
||||
set BU_CDP_URL=http://127.0.0.1:9223 && browser-harness
|
||||
```
|
||||
|
||||
## Navigate and detect session state
|
||||
|
||||
```python
|
||||
new_tab("https://<E10_BASE>/login")
|
||||
wait_for_load()
|
||||
wait(3)
|
||||
capture_screenshot("<SCREENSHOT_DIR>/e10_login.png")
|
||||
print("URL:", js("window.location.href"))
|
||||
```
|
||||
|
||||
If the URL is not `/login`, an existing session may already be authenticated in the isolated profile. Continue only if the current page confirms a valid E10 session.
|
||||
|
||||
## Captcha handling
|
||||
|
||||
E10 captcha is expected to be a four-digit number. If visual recognition returns fewer than four digits, refresh the captcha image and capture again.
|
||||
|
||||
```python
|
||||
capture_screenshot("<SCREENSHOT_DIR>/e10_captcha.png")
|
||||
```
|
||||
|
||||
Use available visual inspection capability to read the four digits. If incomplete, click the captcha image center and retry.
|
||||
|
||||
## Fill login form reliably
|
||||
|
||||
E10 input fields may not respond to direct `element.value = ...`. Use the native input setter and dispatch events.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
info = json.loads(js('''
|
||||
var inputs = document.querySelectorAll("input");
|
||||
var result = [];
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
var r = inputs[i].getBoundingClientRect();
|
||||
result.push({
|
||||
dataId: inputs[i].getAttribute("data-id"),
|
||||
x: Math.round(r.left + r.width/2),
|
||||
y: Math.round(r.top + r.height/2)
|
||||
});
|
||||
}
|
||||
var btn = document.querySelector("button");
|
||||
var br = btn ? btn.getBoundingClientRect() : null;
|
||||
return JSON.stringify({inputs: result, btn: br ? {x: Math.round(br.left+br.width/2), y: Math.round(br.top+br.height/2)} : null});
|
||||
'''))
|
||||
|
||||
js('''
|
||||
var ns = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
|
||||
window.__fi = function(dataId, value) {
|
||||
var el = document.querySelector("[data-id='" + dataId + "']");
|
||||
el.focus();
|
||||
el.value = "";
|
||||
ns.call(el, value);
|
||||
el.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
el.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
el.dispatchEvent(new Event("blur", { bubbles: true }));
|
||||
};
|
||||
''')
|
||||
|
||||
u = info["inputs"][0]
|
||||
p = info["inputs"][1]
|
||||
c = info["inputs"][2]
|
||||
b = info["btn"]
|
||||
|
||||
click_at_xy(u["x"], u["y"]); wait(0.2)
|
||||
js('window.__fi("' + u["dataId"] + '", "<USERNAME>")')
|
||||
click_at_xy(p["x"], p["y"]); wait(0.2)
|
||||
js('window.__fi("' + p["dataId"] + '", "<PASSWORD>")')
|
||||
click_at_xy(c["x"], c["y"]); wait(0.2)
|
||||
js('window.__fi("' + c["dataId"] + '", "<CAPTCHA>")')
|
||||
|
||||
print(js('''
|
||||
var vals = [];
|
||||
document.querySelectorAll("input").forEach(function(el){ vals.push(el.value ? "set" : "empty"); });
|
||||
JSON.stringify(vals);
|
||||
'''))
|
||||
|
||||
click_at_xy(b["x"], b["y"])
|
||||
wait(5)
|
||||
wait_for_load()
|
||||
print("After login:", js("window.location.href"))
|
||||
```
|
||||
|
||||
If still on `/login`, assume captcha or credentials failed. Refresh captcha and retry rather than proceeding.
|
||||
|
||||
## Cleanup
|
||||
|
||||
Linux/macOS:
|
||||
|
||||
```bash
|
||||
pkill -f "chrome.*9223" 2>/dev/null
|
||||
```
|
||||
|
||||
Windows:
|
||||
|
||||
```bat
|
||||
taskkill /F /IM chrome.exe 2>nul
|
||||
```
|
||||
@@ -0,0 +1,155 @@
|
||||
# OpenAPI Documentation Navigation
|
||||
|
||||
Use this reference to inspect E10 OpenAPI documentation pages.
|
||||
|
||||
## Documentation homepage
|
||||
|
||||
Navigate to:
|
||||
|
||||
```text
|
||||
https://<E10_BASE>/sp/openapi/base/doc/index
|
||||
```
|
||||
|
||||
E10 documentation pages are React SPA pages. Plain `curl` usually retrieves only a shell page. Use a rendered browser session and wait for JavaScript content.
|
||||
|
||||
```python
|
||||
new_tab("https://<E10_BASE>/sp/openapi/base/doc/index")
|
||||
wait(8)
|
||||
wait_for_load()
|
||||
wait(3)
|
||||
capture_screenshot("/tmp/e10_api_doc_home.png")
|
||||
```
|
||||
|
||||
## Extract category links
|
||||
|
||||
The documentation homepage exposes categories through `<a>` tags. Extract link text and href values, then navigate directly to the target href instead of clicking.
|
||||
|
||||
```python
|
||||
import json
|
||||
links = json.loads(js('''
|
||||
var all = document.querySelectorAll("a");
|
||||
var result = [];
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
var a = all[i];
|
||||
var txt = a.textContent.trim();
|
||||
if (txt.length > 0 && txt.length < 50 && a.href) {
|
||||
result.push({text: txt, href: a.href});
|
||||
}
|
||||
}
|
||||
return JSON.stringify(result);
|
||||
'''))
|
||||
for l in links:
|
||||
print(l["text"], "->", l["href"])
|
||||
```
|
||||
|
||||
**Pitfall**: The href values extracted from the doc index page (e.g. `https://<E10_BASE>/sp/opendoc/freepass/.../zh_cn/840714220321120257`) may return 404 when navigated to directly. These URLs appear to be for a separate doc viewer that is not directly accessible from the OpenAPI docs SPA. Instead, always navigate through the SPA sidebar — first load the auth/page page (`/sp/opendoc/freepass/.../zh_cn/840673181800431617` which is the "认证" page), then use sidebar JS clicks to reach other API docs. Do NOT attempt to construct doc page URLs from the index page links.
|
||||
|
||||
## Read rendered page text
|
||||
|
||||
After navigating to a category or API page, extract `document.body.innerText`. Long pages may need scrolling and repeated extraction.
|
||||
|
||||
```python
|
||||
content = js("document.body.innerText")
|
||||
print(content[:15000])
|
||||
```
|
||||
|
||||
If content appears incomplete:
|
||||
|
||||
```python
|
||||
js("window.scrollBy(0, 800)")
|
||||
wait(2)
|
||||
print(js("document.body.innerText")[8000:15000])
|
||||
```
|
||||
|
||||
## Sidebar navigation (SPA — JS click required)
|
||||
|
||||
The documentation page is a React SPA. Sidebar tree items use class `.ui-tree-bar` and do not respond reliably to ref-based `browser_click`. Use JavaScript to expand categories and navigate to sub-items.
|
||||
|
||||
### Expand a category
|
||||
|
||||
```python
|
||||
# Expand "工作流程" category
|
||||
js('''
|
||||
(function() {
|
||||
var items = document.querySelectorAll(".ui-tree-bar");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].textContent.trim() === "工作流程") {
|
||||
items[i].click();
|
||||
return "expanded";
|
||||
}
|
||||
}
|
||||
return "not found";
|
||||
})();
|
||||
''')
|
||||
```
|
||||
|
||||
### Navigate to a sub-item
|
||||
|
||||
After expanding the parent category, navigate by clicking on the sub-item text element (class `.ui-tree-node`):
|
||||
|
||||
```python
|
||||
# Click "创建流程实例" sub-item
|
||||
js('''
|
||||
(function() {
|
||||
var items = document.querySelectorAll(".ui-tree-bar");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].textContent.trim() === "创建流程实例") {
|
||||
items[i].click();
|
||||
return "clicked";
|
||||
}
|
||||
// Also try clicking the .ui-tree-node child
|
||||
var node = items[i].querySelector(".ui-tree-node");
|
||||
if (node && node.textContent.trim() === "创建流程实例") {
|
||||
node.click();
|
||||
return "clicked node";
|
||||
}
|
||||
}
|
||||
return "not found";
|
||||
})();
|
||||
''')
|
||||
```
|
||||
|
||||
The sub-items appear as `<li>` elements with class `.ui-tree-bar` at `level=2` inside the parent category's `<ul>`. Ref-based clicking (`browser_click` on `@eNNN`) is unreliable for these SPA elements — prefer JS `.click()` through `browser_console`.
|
||||
|
||||
### Content extraction after navigation
|
||||
|
||||
After clicking a sidebar item, verify the main content updated by checking `document.body.innerText.length` — it should grow significantly. Then extract the API documentation from the second occurrence of the item name (the first is the sidebar label itself).
|
||||
|
||||
```python
|
||||
# Find the second occurrence (main content, not sidebar)
|
||||
idx = js('document.body.innerText.indexOf("创建流程实例", document.body.innerText.indexOf("创建流程实例") + 6)')
|
||||
content = js('document.body.innerText.substring(' + str(idx) + ')')
|
||||
```
|
||||
|
||||
## Avoid confusing API names
|
||||
|
||||
Workflow-related documentation commonly contains similar names:
|
||||
|
||||
- `新增流程`: creates a workflow definition or template. This is an administrator operation and is not the external integration target.
|
||||
- `创建流程实例` or APIs under `工作流程`: creates workflow instances. Use this for external system integration.
|
||||
|
||||
Note: the OpenAPI doc endpoint for creating workflow instances is `doCreateRequest` (`/papi/openapi/api/workflow/core/paService/v2/doCreateRequest`). The sidebar label is "创建流程实例" but the page title may show "新增流程" — they refer to the same API. Do not confuse with the "新增流程" sidebar item under "业务表单" which is for form template creation.
|
||||
|
||||
## OAuth2 documentation
|
||||
|
||||
E10 OpenAPI authentication is OAuth2 code-to-token flow. It is not a Bearer-token-header pattern.
|
||||
|
||||
Document these endpoints when relevant:
|
||||
|
||||
```text
|
||||
POST /papi/openapi/oauth2/authorize
|
||||
POST /papi/openapi/oauth2/access_token
|
||||
```
|
||||
|
||||
The `access_token` is passed as a request-body parameter in business API calls.
|
||||
|
||||
## File upload documentation
|
||||
|
||||
If a workflow contains attachment or file fields, inspect file upload documentation. It is typically under a knowledge-documentation or file-upload category, not necessarily a top-level homepage item.
|
||||
|
||||
The usual pattern is:
|
||||
|
||||
1. Call file upload API to obtain a file identifier.
|
||||
2. Reference the file identifier in business API payload fields through `dataOptions`, including file-specific metadata.
|
||||
|
||||
Only include file upload prerequisites when the target API or workflow fields require attachments.
|
||||
@@ -0,0 +1,120 @@
|
||||
# Output Document Standard
|
||||
|
||||
Use this reference to validate final Markdown API documentation.
|
||||
|
||||
## Audience
|
||||
|
||||
Write for external integration developers. They need stable API behavior, authentication, request payloads, response payloads, examples, and field mapping. They do not need internal E10 management steps or implementation details.
|
||||
|
||||
## Required sections
|
||||
|
||||
The final document should contain:
|
||||
|
||||
1. Interface title.
|
||||
2. Interface description.
|
||||
3. Prerequisites.
|
||||
4. OAuth2 access_token acquisition flow.
|
||||
5. Identity mapping rules.
|
||||
6. Request information.
|
||||
7. Request parameters.
|
||||
8. Request examples.
|
||||
9. Response parameters.
|
||||
10. Response examples.
|
||||
11. Error codes.
|
||||
12. Workflow field description when documenting workflow creation.
|
||||
13. File upload prerequisite section only when attachments are involved.
|
||||
|
||||
## Authentication rules
|
||||
|
||||
Always document the two-step OAuth2 flow:
|
||||
|
||||
1. `POST /papi/openapi/oauth2/authorize` to obtain `code`.
|
||||
2. `POST /papi/openapi/oauth2/access_token` to exchange `code` for `access_token`.
|
||||
|
||||
State that `access_token` is passed in the request body of business APIs, not in an HTTP Authorization header.
|
||||
|
||||
## Identity mapping rules
|
||||
|
||||
External systems usually do not hold E10 internal user or department IDs. Include a dedicated identity mapping section.
|
||||
|
||||
Top-level user identifier options:
|
||||
|
||||
| Parameter | Meaning |
|
||||
|---|---|
|
||||
| `JOB_NUM` | Employee job number. Preferred for personnel integrations when job numbers are stable. |
|
||||
| `EMAIL` | Employee email. |
|
||||
| `MOBILE` | Employee mobile number. |
|
||||
| `idNos` | Identity document number. |
|
||||
| `loginID` | E10 login ID. |
|
||||
| `account` | Account value supported by the target E10 configuration. |
|
||||
|
||||
Department identifier options:
|
||||
|
||||
| Parameter | Meaning |
|
||||
|---|---|
|
||||
| `DEPT_CODE` | Department code. Preferred when external systems maintain department codes. |
|
||||
| `DEPT_NAME` | Department name. Use only when names are unique and stable. |
|
||||
|
||||
For form fields:
|
||||
|
||||
- Employee fields should explain `dataOptions[].type: "resource"` and compatible `userType` values.
|
||||
- Department fields should explain `dataOptions[].type: "department"` and compatible `deptType` values.
|
||||
- File fields should explain upload-before-submit behavior.
|
||||
|
||||
## URL and placeholder rules
|
||||
|
||||
Use:
|
||||
|
||||
```text
|
||||
https://<E10_BASE>
|
||||
<ACCESS_TOKEN>
|
||||
<APP_KEY>
|
||||
<APP_SECRET>
|
||||
<CORP_ID>
|
||||
<CODE>
|
||||
```
|
||||
|
||||
Do not use real hostnames, IP addresses, accounts, passwords, cookies, tokens, app secrets, database table names, or internal IDs in external documentation.
|
||||
|
||||
## Request examples
|
||||
|
||||
Provide at least two examples for workflow instance creation:
|
||||
|
||||
- Minimal request: only required fields and the smallest valid payload.
|
||||
- Full request: representative optional fields, identity mapping, and detail rows if available.
|
||||
|
||||
Examples must use external identifiers such as `EMP001` and `DEPT_TECH`, not E10 internal IDs.
|
||||
|
||||
## Field table rules
|
||||
|
||||
Use this table header:
|
||||
|
||||
```markdown
|
||||
| 字段名 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
```
|
||||
|
||||
Requiredness should be:
|
||||
|
||||
- `是` when confirmed.
|
||||
- `否` when confirmed optional.
|
||||
- `需确认` when not available from docs, form rules, or user metadata.
|
||||
|
||||
Group fields by business meaning. Do not copy E10 internal group names such as “未分组” or technical database groupings unless the user explicitly wants internal documentation.
|
||||
|
||||
## Forbidden final-document content
|
||||
|
||||
The final external-facing document must not contain:
|
||||
|
||||
- E10 version numbers.
|
||||
- Internal API paths under `/api/...`.
|
||||
- Internal OpenAPI document page IDs.
|
||||
- Database table names or column names.
|
||||
- Browser-harness commands.
|
||||
- XHR interception details.
|
||||
- Screenshot paths.
|
||||
- Login page operation instructions.
|
||||
- Generated-at metadata.
|
||||
- Informal labels such as “坑”, “注意”, “陷阱”, “提示”.
|
||||
|
||||
Convert operational caveats into formal constraints, parameter notes, or prerequisites.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Troubleshooting
|
||||
|
||||
Use this reference when live extraction or documentation quality checks fail.
|
||||
|
||||
## Browser and session
|
||||
|
||||
- Browser-harness daemon may persist across commands. Always set `BU_CDP_URL` in the same command as `browser-harness`.
|
||||
- Clear stale daemons before a new task.
|
||||
- Use an isolated Chrome profile and port. Never attach to the user's daily browser.
|
||||
- If headless screenshots are blank, wait and capture again.
|
||||
- CloakBrowser-style Playwright wrappers may not expose the CDP endpoint expected by browser-harness.
|
||||
|
||||
## Login
|
||||
|
||||
- E10 input fields often require native setter plus `input`, `change`, and `blur` events.
|
||||
- Locate inputs through `data-id`, not placeholder or name attributes.
|
||||
- Captcha is expected to be four digits. If fewer digits are recognized, refresh the captcha and retry.
|
||||
- After login, verify the URL is no longer the login page before extracting docs.
|
||||
|
||||
## OpenAPI pages
|
||||
|
||||
- E10 docs are React SPA pages. Wait after navigation before extracting `document.body.innerText`.
|
||||
- `curl` may return only the shell HTML and is not reliable for page content.
|
||||
- Documentation category entries are often `<a>` tags; extract hrefs and navigate directly.
|
||||
- Distinguish “新增流程” from “创建流程实例”. Use the workflow-instance API for external workflow submission.
|
||||
|
||||
## Workflow and forms
|
||||
|
||||
- Prefer internal read-only APIs over UI clicking.
|
||||
- `workflowId` is not `formId`.
|
||||
- Workflow names and form names may differ. Use core keywords.
|
||||
- `pageNo` and `pageSize` are the expected pagination keys, not `page` and `size`.
|
||||
- `formFieldSearchEntity` is required for field queries.
|
||||
- If `formTableId` returns no main-table fields, retry without `formTableId`.
|
||||
- Use `getForm` to detect detail tables before trying UI switching.
|
||||
|
||||
## UI fallback
|
||||
|
||||
- JS `click()` may fail on workflow cards. Coordinate click may be required.
|
||||
- Multiple menu items have similar names. Confirm the current page and tab before extracting fields.
|
||||
- `字段设置` and `字段管理` are not the same.
|
||||
- Pagination can be unreliable; prefer requesting enough records through the internal field API.
|
||||
|
||||
## Output quality
|
||||
|
||||
- If requiredness cannot be confirmed, write `需确认`.
|
||||
- If field type mapping is unclear, describe the known field type and mark payload mapping as needing confirmation.
|
||||
- If a workflow has no detail table, omit the detail-table section entirely.
|
||||
- If generated documentation contains internal API paths or database names, revise before delivery.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Workflow Field Discovery
|
||||
|
||||
Use this reference when the user asks for documentation for creating a specific E10 workflow instance.
|
||||
|
||||
## Preferred path
|
||||
|
||||
Prefer internal read-only APIs over UI clicking:
|
||||
|
||||
1. Call `getBaseInfoListTree` to locate the workflow and workflowId.
|
||||
2. Call `getFormList` with the core workflow keyword to locate the formId.
|
||||
3. Call `getForm` to read form metadata and detect detail tables.
|
||||
4. Call `getFormFieldPage` to retrieve field definitions.
|
||||
5. Use UI and DOM extraction only as a fallback.
|
||||
|
||||
See `internal-apis.md` for endpoints and response structure.
|
||||
|
||||
## Matching workflow names
|
||||
|
||||
Workflow titles may include category numbers, prefixes, or punctuation. Search using both:
|
||||
|
||||
- Exact user-provided name.
|
||||
- Core keyword after removing numbering, punctuation, and common suffixes such as “流程”.
|
||||
|
||||
If multiple workflows match, show the candidates and ask the user to choose.
|
||||
|
||||
## Matching forms
|
||||
|
||||
`workflowId` and `formId` are different entities. Never substitute one for the other.
|
||||
|
||||
Form names may be shorter than workflow names. For example, a workflow title may include a prefix while the form name may not. Use `getFormList` with a core keyword, then confirm by comparing the form name to the target workflow.
|
||||
|
||||
## Main table and detail table handling
|
||||
|
||||
Use `getForm` to determine whether the form has detail tables.
|
||||
|
||||
- If no detail table exists, omit the detail-table section in the final documentation.
|
||||
- If detail tables exist, retrieve each detail table's fields and document them in separate business sections.
|
||||
- If a detail table exists but field extraction fails, mark the detail table fields as “需确认” rather than inventing field definitions.
|
||||
|
||||
## Field extraction quality
|
||||
|
||||
For each field, capture at least:
|
||||
|
||||
- Display name: `title`.
|
||||
- Payload key: `dataKey`.
|
||||
- Field type: `type`.
|
||||
- Requiredness if available from OpenAPI docs, form rules, or explicit user-provided metadata.
|
||||
- Business meaning if inferable from name and context.
|
||||
|
||||
Do not include internal values such as database table names, `columnName`, management UI group names, or internal API paths in final documentation.
|
||||
|
||||
## UI fallback
|
||||
|
||||
Use UI fallback only if internal read-only APIs fail.
|
||||
|
||||
General fallback path:
|
||||
|
||||
1. Navigate to `/info/engine_wf/pathdef/list/company`.
|
||||
2. Locate the target workflow card.
|
||||
3. Use coordinate click when JS `click()` fails.
|
||||
4. Enter `表单管理` then `字段管理`.
|
||||
5. Extract table rows or intercept XHR requests.
|
||||
|
||||
E10 workflow UI has multiple similarly named navigation items. Distinguish:
|
||||
|
||||
- `表单管理` top tab inside workflow detail page.
|
||||
- `字段管理` sub-tab inside form management.
|
||||
- `字段设置` is not `字段管理`.
|
||||
- `主表` and `明细表` are table-level tabs after entering field management.
|
||||
|
||||
## Fallback XHR interception
|
||||
|
||||
If UI must be used to discover `formTableId`, inject an XHR hook before switching the main/detail table tab, then inspect captured request bodies for `getFormFieldPage`.
|
||||
|
||||
Use this only for internal extraction. Do not mention XHR interception in the final external documentation.
|
||||
Reference in New Issue
Block a user