# 模块定义开发

定义模块属性、方法、事件、用户域和权限。

模块版本来自项目 Git 快照。运行时可调用方法来自 `methods/*.json`，不能只修改数据库记录、Manager 页面或已经安装的运行态服务。

模块源码项目不是发布单元，可以先独立创建：

```bash
baijimu module project create \
  --workspace-id <workspaceId> \
  --name <projectName>
```

模块定义必须在 Bundle 内创建。`<bundle>` 可以是 `bundleId` 或精确名称；CLI 会先解析
并校验唯一 Bundle，再把稳定 `bundleId` 写入模块定义：

```bash
baijimu bundle module create <workspace> <bundle> \
  --project-id <projectId> \
  --name <moduleName> \
  --description <description>
```

不要使用隐藏的旧版顶层 `module create` 作为新流程入口，也不要创建没有 Bundle 归属的
模块记录。

## 主要文件

| 文件               | 内容                      |
| ---------------- | ----------------------- |
| `module.json`    | 名称、描述、属性、事件、用户域、接口和兼容配置 |
| `methods/*.json` | 方法名称、参数、返回值、错误类型和方法实现   |

不同历史项目中的 JSON 字段可能使用数组或 JSON 字符串。修改时保留现有字段形状，除非当前版本迁移明确要求规范化。

## 属性

属性需要声明类型、必填、默认值、敏感性和管理方。常见分类：

- 连接凭据：`appId`、`appSecret`、`token`
- 路由配置：`serviceBaseUrl`
- 运行开关：`autoStart`、`syncEnabled`
- 运行态回填：外部实例 ID、状态、最后同步时间

敏感属性不能出现在页面文案、日志、Bundle Manifest 或普通返回值中。

模块需要引用另一个模块时，使用 `Interface` 属性和 `ServiceReference` 逻辑值；需要保证目标
Bundle 一同安装时，在 Bundle Manifest 中声明精确 `dependencies`。不要使用历史模块依赖字段
代替这两层契约。安装时可用属性默认值绑定目标，安装后可用 `baijimu runtime app properties update`
手动绑定；Runtime
管理引用访问关系，插件通过新协议按需领取当前凭据。完整方式见
[ServiceReference 声明、绑定与运行时交付](/development/bundle-development/module-development/service-reference/)。

需要通过稳定业务键绑定多个同接口目标时，属性类型使用 `InterfaceMap`，属性值使用
`ServiceReferenceMap`。两者的 JSON 结构、安装后配置、整组替换和按需引用访问解析规则见
[InterfaceMap 与 ServiceReferenceMap](/development/bundle-development/module-development/interface-map/)。

## 方法

每个方法应明确：

- 稳定业务名称
- 参数类型、必填和默认值
- 返回类型和错误模型
- 需要的属性与用户域
- 超时、幂等和重试语义

不要把内部 Controller 名、第三方原始路径或临时实现细节作为公共方法名。

`paramDefinitions` 是模块方法面对所有调用方的公共 JSON 输入契约。平台应用页面发送的
JSON 顶层字段与参数名称一一对应；模块内部 HTTP 请求的 `methodBody` 不属于页面协议。
通过 CLI 创建 HTTP 方法时使用 `--type HttpMethod`；这里的 `HttpMethod` 是平台方法实现类型，
不是 `GET` 或 `POST`。HTTP 动词写入 `methodBody.http_method`，查询参数映射写入
`methodBody.query`，不能把 `GET`、`POST` 或 `QUERY` 传给 `--type`。
`methodBody` 的可编辑源必须使用 snake\_case，具体字段、示例和历史兼容边界见
[HTTP methodBody 源契约](/development/bundle-development/module-development/http-method-body/)。
浏览器侧的完整调用方式见
[平台应用调用模块方法](/development/bundle-development/platform-application-development/module-method-calls/)。

## 事件

后端模块事件是 `module.json` 顶层 `events` 数组中的版本化声明，不使用独立的
`events/*.json` 文件。事件的创建、修改和删除都通过修改这个数组完成，不要求独立的事件
CRUD 命令。

```json
{
  "events": [
    {
      "name": "orderStatusChanged",
      "description": "订单状态发生变化后触发。",
      "paramDefinitions": [
        {
          "name": "orderId",
          "type": {
            "@type": "DataType",
            "type": "string",
            "nullable": false
          },
          "description": "订单 ID。",
          "required": true
        },
        {
          "name": "status",
          "type": {
            "@type": "DataType",
            "type": "string",
            "nullable": false
          },
          "description": "变更后的订单状态。",
          "required": true
        },
        {
          "name": "occurredAt",
          "type": {
            "@type": "DataType",
            "type": "integer",
            "nullable": false
          },
          "description": "事件发生时间，Unix epoch 毫秒。",
          "required": true
        }
      ]
    }
  ]
}
```

新定义必须把 `events` 写成实际 JSON 数组，不要写成包含 JSON 文本的字符串。事件和参数名称
是调用方、订阅方和触发规则共同依赖的稳定契约；已经发布后若重命名或改变参数类型，应创建
明确的后继事件，而不是静默改变原事件语义。

`paramDefinitions` 描述事件载荷的顶层参数，类型结构与模块方法参数相同：

- `required` 表示该顶层参数是否必须出现。
- `nullable` 表示参数出现时其值能否为 `null`。
- 对象和数组可以继续使用 `properties`、`items`、`required`、`additionalProperties` 等
  `DataType` 字段描述内部结构。
- 平台控制的时间字段统一使用 Unix epoch 毫秒整数，完整规则见
  [时间类型](/development/data-contracts/time/)。

模块事件声明不同于本地 Connector 的 `payload_schema`，不要在两种能力之间复制字段名。
声明事件只会把事件加入模块版本和安装后的服务定义，不会自动发送事件，也不会自动创建
订阅或动作。事件生产方必须通过目标 Runtime 提供的事件发布能力发送与声明一致的载荷；
需要把事件绑定到模块方法或其他动作时，应把 Event Trigger 作为独立的 Bundle 资源管理。
工作流和独立模块后端的发布方式、地址来源、回调属性及安全边界见
[模块事件与发布机制](/development/bundle-development/module-development/event-development/)。

提交时检查并只提交预期源码，再使用该提交创建模块版本：

```bash
baijimu project checkout <projectId> --workspace-id <workspaceId> --directory <directory>
cd <directory>
git diff -- module.json
git add -- module.json
git commit -m '<message>'
git push
commitId="$(git rev-parse HEAD)"

baijimu bundle module version create <workspace> <bundle> <projectId> \
  --module-id <moduleId> \
  --version <semanticVersion> \
  --commit-id <commitId>
```

版本创建后应从版本定义或安装后的 Runtime 服务中回读事件名称和 `paramDefinitions`，确认它们来自
预期 Git 提交。只有整个 Bundle 的版本创建、市场审核和安装完成后，新事件才会进入目标 Runtime；
仅修改工作区文件或创建模块版本都不会升级已经安装的 Bundle。

## 用户身份

模块方法从运行时调用上下文获取当前 Runtime、已验证调用方、工作区和访问用户。不要依赖浏览器把 `userId` 作为普通业务参数传入。

需要映射第三方账号时，服务端保存百积木用户与外部账号的映射，并在调用前同时校验平台权限和外部系统权限。

当前可用字段、不同入口下的出现条件以及 HTTP 方法映射方式见
[模块调用上下文](/development/bundle-development/module-development/runtime-context/)。

## 与插件开发的边界

模块声明属性、方法、事件和接口引用，不拥有 Bundle 的安装状态机。需要独立后端协调外部状态或
按需领取 Runtime 引用凭据时，统一使用[生命周期插件开发](/development/lifecycle-plugin-development/)。
插件注册、请求协议、阶段处理和安装验证均在该入口维护；模块文档仅负责资源声明和服务调用。

创建模块版本遇到插件退役或安装时协议不匹配，请按
[旧插件迁移与发布错误处理](/development/lifecycle-plugin-development/retired-plugin-migration/)
定位精确源码与失败资源，完成后继版本迁移；不能只升级 CLI 或替换插件名称。
