跳转到内容

后端业务开发

业务代码放在应用的 server/app,平台实现由 github.com/igwen6w/modu/server 提供。完整的业务示例(商品与物料附件、货币管理、员工报销与审批)已迁入独立示例插件仓库 igwen6w/modu-example-plugin,通过 Manifest app.module/example 接入;开始新模块时,可参考该插件的 Go 包 github.com/igwen6w/modu-example-plugin/server(导出 exampleplugin.Options())与前端包 @igwen6w/modu-example-web。默认 starter 只包含内置扩展,不含示例插件。

server/application/options.go 是应用唯一装配入口:

字段注册内容
Configure应用配置覆盖
Routes、Menus业务 API、菜单与权限声明
Authorization、AuthorizationDefaults统一授权的资源、能力与来源声明,以及默认安装项(仅首次安装生效)
Migrations、Seeders应用表结构与初始化数据
DataTransfers数据交换合同
AttachmentFields业务附件字段
Resources、FakeScenariosAgent 资源与开发测试场景
Deployment部署维护声明(访问策略与就绪要求)
Extensions应用自有扩展声明及惰性注册

应用只追加自己的能力。核心自动提供平台内置扩展,无需复制注册清单。

下面是示例插件中货币模块列表与创建路由的写法:

func Routes() []sdk.Route {
c := NewCurrencyController()
return []sdk.Route{
{
Method: "GET",
Path: "currencies",
Permission: "example:currency:list",
Handler: c.Index,
},
{
Method: "POST",
Path: "currencies",
Permission: "example:currency:create",
Handler: c.Store,
},
}
}

此代码位于示例插件的货币 package(server/app/example/currency),导入 github.com/igwen6w/modu/server/sdk。路径相对 /api/v1,实际地址为 /api/v1/currencies。权限码在权限控制台中归属于「示例 / 货币」「示例 / 报销」等分组,写法可参考示例插件 server/app/example/currency/authorization.go 与 expense/authorization.go 的授权声明。普通业务路由必须有非空权限,统一继承 JWT、操作日志与权限检查。

装配阶段拒绝核心、应用及扩展之间的路由冲突。不要另开绕过装配的业务入口。

控制器负责校验输入、获取当前用户和返回统一响应;服务负责查询、事务和业务规则。常用 SDK 包括:

  • sdk.CurrentUserID(ctx):读取已认证的当前用户。
  • sdk.Pagination(ctx):读取 page 与 pageSize。
  • sdk.Success、sdk.Error:返回统一响应。
  • 统一授权投影:可见拥有者与记录边界由授权投影与强制边界统一计算,不在业务代码中手工拼装拥有者集合。

数据访问范围在业务服务中显式应用。列表筛选、详情、更新、删除和批量操作均应检查边界,不能只过滤列表。

带文件的数据交换通过 sdk.DataTransfer* 合同声明,并在 bootstrap.Options.DataTransfers 中注册;业务附件字段通过 sdk.AttachmentField 与 sdk.AttachmentContext 声明,写入时使用 sdk.ReplaceAttachments 整体替换附件集合,避免部分更新导致的数据不一致。

通过同一 Options 注册 sdk.Menu、迁移和种子。页面路径以父目录路径为前缀,业务权限码与路由声明一致。

Agent 业务通过 sdk.ResourceDescriptor 注册,并可通过 ResourceDescriptor.PrepareCreate 提供创建前的回调(供 Agent 场景校验或补全字段)。HTTP 与 Agent 复用同一个业务用例,保持权限、数据范围、字段规则与幂等行为一致。Agent 侧的可选资源通过 sdk.AuthorizationResourceSelector 暴露给授权选择器。

普通业务依赖 Goravel 公开 contracts、facades 与核心 sdk。应用装配使用 bootstrap,扩展使用公开扩展合同。

不导入核心 app/models、app/services 或 Goravel 内部实现包。缺少必要接口时,在核心定义窄接口并通过新包版本交付,避免业务与核心源码重新耦合。