# 调用模块方法

平台应用前端通过标准 SDK 和 App Gateway 逐方法路由调用已授权的 Runtime 模块方法。

平台应用前端调用模块能力时，统一通过 app-gateway。页面不能直接请求模块服务地址或平台内部接口。

当前部署的对外 App Gateway 前缀是 `/app-gateway/api`；`/lowcode3/api` 是历史或其他
部署形态的路径，不能作为当前环境的 API 地址。完整的网关选择、授权链和配置方法见
[App Gateway 接入与配置](/development/bundle-development/platform-application-development/app-gateway-configuration/)。

## 调用协议

```http
POST /app-gateway/api/workspace-runtime/apps/{installId}/invoke
Authorization: Bearer {appClientToken}
Content-Type: application/json
```

`installId` 是当前工作区的平台应用安装标识。请求中的 `target` 和 `method` 必须与当前不可变
Platform Application 版本的 `methodDefinitions` 完全一致。标准 SDK 从启动会话取得 `installId`。

## 请求参数

POST 请求体包含声明目标、方法名和模块方法参数对象：

```http
POST /app-gateway/api/workspace-runtime/apps/pia_xxx/invoke
Authorization: Bearer ag1_xxx
Content-Type: application/json
```

```json
{
  "target": {
    "type": "module",
    "bundleId": "course-management",
    "moduleId": "course-directory"
  },
  "method": "createCourse",
  "params": {
    "code": "COURSE-001",
    "name": "高等数学",
    "teacherId": 123,
    "tags": [
      "必修",
      "一年级"
    ]
  }
}
```

`params` 是方法参数对象，其中字段与模块方法 `paramDefinitions` 中的参数名称一一对应：

- 必填、类型、对象结构和数组元素服从模块方法定义。
- 无参数方法的 `params` 也必须是 JSON 空对象 `{}`。
- 不要传递 Runtime `service`、`methodBody`、`userId` 或平台身份请求头。
- `methodBody` 只描述特定模块实现如何访问后端 HTTP 服务，不属于前端调用协议。

例如 `createCourse` 定义了 `code`、`name`、`teacherId` 和 `tags`，页面就发送同名
JSON 字段。模块内部是否把 `teacherId` 转换为 `teacher_id`，属于模块实现，不影响
页面协议。

## module 与 interface

固定依赖应直接使用 Module 领域引用。声明和调用使用同一个 target：

```json
{
  "target": {
    "type": "module",
    "bundleId": "course-management",
    "moduleId": "course-directory"
  },
  "methods": [
    "listWorkspaceUsers",
    "createCourse"
  ]
}
```

只有当业务明确需要可替换实现时才使用 interface。例如版本声明：

```json
{
  "target": {
    "type": "interface",
    "name": "course-directory"
  },
  "methods": ["listWorkspaceUsers", "createCourse"]
}
```

Bundle 平台应用资源声明的接口来源为：

```json
{
  "interfaceBindings": {
    "course-directory": {
      "bundleId": "course-management",
      "moduleId": "course-directory"
    }
  }
}
```

安装计划根据当前 Bundle 及其依赖 Bundle 的模块领域引用 解析接口。具体 Runtime service 绑定值和
调用身份只保存在安装态，由平台动态维护，不进入平台应用源码、Bundle Manifest 或浏览器。
无论选择 module 还是 interface，原始 `service`、具体 `businessId` 和 `*` 通配声明都不能用于新版本。

## 查询当前工作区用户

平台应用没有“登录后可以任意调用平台后端模块”的权限。平台已经通过 `workspace-core` 的
`工作区管理` Module 提供 `listWorkspaceMembers`；业务 Bundle 必须显式依赖 `workspace-core`，
平台应用版本再声明并绑定这个方法。页面仍然通过标准 SDK 调用：

```ts
const members = await client.call(
  {
    type: "module",
    bundleId: "workspace-core",
    moduleId: "2108",
  },
  "listWorkspaceMembers",
  {},
);
```

方法从经过验证的调用上下文取得当前工作区和当前用户，不接受页面传入的 `workspaceId` 或
`userId` 作为可信身份。依赖声明、真实 领域引用、应用能力绑定、发布安装和验证步骤见
[示例：查询工作区成员](/development/bundle-development/platform-application-development/workspace-member-list-example/)。

## 响应

调用成功时，app-gateway 返回统一 CModel 响应，模块方法返回值位于 `data`：

```json
{
  "contractVersion": "1.0.0",
  "errorCode": "0",
  "data": {
    "id": 1001,
    "code": "COURSE-001",
    "name": "高等数学"
  }
}
```

调用方收到 HTTP `200` 后，根据 `errorCode` 判断业务结果，再读取成功响应的 `data`。常见失败边界由稳定错误码表达：

| 错误码类别 | 含义                                |
| ----- | --------------------------------- |
| 参数错误  | target、method 或 JSON 参数格式错误       |
| 未授权   | 缺少、过期或无效的 `appClientToken`        |
| 禁止访问  | token 不属于当前安装、应用未声明方法，或当前用户没有方法权限 |
| 上游失败  | 模块运行时或内部调用链返回失败                   |

业务组件只在 SDK 确认 App Gateway 会话失效并清空 `client.getSession()` 后重新授权。后端业务 CModel
`UNAUTHORIZED` 不代表登录失效，即使 HTTP 状态为 401 也应保留会话。详见
[错误与会话处理](/development/bundle-development/platform-application-development/sdk-and-runtime/#错误与会话处理)。

## 使用标准 SDK

业务组件不得自行拼接授权头、平台地址或调用 URL，应复用应用启动时创建的标准客户端：

```ts
const course = await client.call(
  {
    type: "module",
    bundleId: "course-management",
    moduleId: "course-directory",
  },
  "createCourse",
  {
    code: "COURSE-001",
    name: "高等数学",
    teacherId: 123,
    tags: ["必修", "一年级"],
  },
);
```

客户端由 `@baijimu/platform-application-sdk` 提供。它读取当前安装会话、发送 Bearer token、解析严格
CModel 响应，并在 `401` 时清除当前会话。不同部署环境的 `appGatewayApiBase` 由应用部署配置注入，
不能在业务组件中散落生产域名。

## 调用前提

一次调用只有同时满足以下条件才会执行：

1. `appClientToken` 有效且属于 URL 中的 `installId`。
2. 安装记录处于启用状态，并具有对应运行态应用。
3. 当前平台应用版本声明了请求中的 `target` 和 `method`。
4. module 或 interface target 已在安装态解析到具体 Runtime 服务，并具有有效的安装态调用身份。
5. 当前工作区用户拥有调用该模块方法的权限。
6. 目标模块已由当前 Bundle 或其已解析依赖 Bundle 安装物化且方法可用。

App Gateway 通过这些检查并完成安装态解析后，才把方法和请求参数转换为内部运行时调用。前端不需要
了解内部调用信封或模块的后端 HTTP 实现。

新应用统一使用结构化 target 调用入口；`/services/{service}/methods/{method}` 只服务历史 SDK 2.x
应用。不要把历史 `/lowcode3/api`、静态站点域名或
其他环境地址拼接为当前应用的 API 地址。
