87 lines
7.3 KiB
Markdown
87 lines
7.3 KiB
Markdown
# 应用接口、数据与后台任务
|
||
|
||
## API 和 CMD
|
||
|
||
`module/system/health/api/admin/status.dever` 是一个可匿名访问的健康检查:
|
||
|
||
源码:[健康检查 API](../assets/examples/http-rest/module/system/health/api/admin/status.dever)
|
||
|
||
```dever
|
||
public get ready = app.status
|
||
```
|
||
|
||
API 只把请求交给 App,不写函数体。参数和返回类型直接使用 App 的声明。HTTP 支持 get/post/put/delete,不支持 patch、旧 get_ 前缀或文件内的 site 声明。
|
||
|
||
`cmd greet = app.greet` 生成 `component.domain.greet` 命令,参数是单个 JSON 对象。无需 main.dever,也无需手写命令行解析。
|
||
|
||
GET/DELETE 从 URL query 读标量参数,POST/PUT 从 JSON body 读参数。多传、重复或缺少必填字段都会报错。无需自己读 body 或构造 `dever.http.Response`。
|
||
|
||
成功返回 `{"code":0,"message":"ok","data":...}`。失败时 data 为 null,code 对应 HTTP 状态码。每个接口绑定一个能序列化的输出,输出名可自定。
|
||
|
||
`dever.api.Error` 中 Invalid/Unauthorized/Forbidden/NotFound/Conflict/TooManyRequests 对应 400/401/403/404/409/429;未知内部失败记录脱敏日志并返回安全的 500。
|
||
|
||
普通写 HTTP、CMD 和 Job 默认由入口建立整体事务。需要事务外编排时,在入口写 `long post generate = app.generate`、`long cmd generate = app.generate` 或 `long job ...`,其业务写库和 Job 入队必须进入显式 `transaction`。`public long` 的匿名权限与事务模式独立;自动 REST 保持原事务行为,GET 不能写库或入队。
|
||
|
||
HTTP、文件传输、外部 Worker 和密码计算放在事务外;事务内重新检查写入依赖的状态。需要业务记录与入队原子提交时,将两者放在同一个 `transaction`。`long` 中前一阶段已经提交的写入不会因为后续处理或响应编码失败而撤销,重试须保证业务幂等;普通入口仍整体回滚。`long` 不延长超时、不自动转后台;编译器遇到边界违规报错,不自动切换模式。
|
||
|
||
## 站点、身份与权限
|
||
|
||
`sites.admin.path = "admin"` 匹配领域内 `api/admin/**`,front 同理。文件名 topic 也进入 URL;上述 ready 是 `/system/health/admin/status/ready`。每条 HTTP 声明只能匹配一个站点,重叠或遗漏配置会失败;path 不改写 URL,也不匹配 `api/admin.dever`。
|
||
|
||
所有 HTTP 默认鉴权并校验精确权限。只有明确匿名操作写 `public post login = app.login` 等单条 public 声明;没有 public rest、public cmd、auth.require 或每个业务文件的权限注册代码。
|
||
|
||
配置中的 auth provider 指向项目自己的 `verify` App 方法,由它核实账号、会话、成员和租户关系。输入是 `dever.auth.Claims`,输出必须且只能有三个字段:
|
||
|
||
- `id: Text`:核验后的主体标识。
|
||
- `user_id: <UserModelId>?`:用户的 Model ID。
|
||
- `tenant_id: <TenantModelId>?`:租户的 Model ID。
|
||
|
||
verify 只能读 global Model,不能写库、拿原始 Header 或返回客户端自报的权限。
|
||
|
||
受保护接口认证成功后,App 可用 `dever.auth.id()`、`session()`、`user_id()`、`tenant_id()` 和 `dever.site.key()` 读取身份,不需要传 context。
|
||
|
||
这些值来自已核验的上下文,不能用客户端提交的 user_id/tenant_id 替代。ID 类型必须匹配 verify 声明,可空 ID 先用分句处理。public、CMD、Job 和普通测试不会自动获得身份读取能力。
|
||
|
||
权限键由核心生成,格式为 `component.domain.site.action`,例如 `news.article.admin.publish`。请求方法记录为元数据,不拼进权限键。REST 的 action 固定为 read/create/replace/delete。
|
||
|
||
核心已经维护角色、用户角色和角色权限表。后台 App 使用 `dever.auth.permissions/save_role/grant_role/revoke_role/disable_role` 管理授权,参数见 [签名索引](signatures.md);这些方法只能由受保护的写 API 调用。`dever.auth.owns_user(id)` 检查资源归属,不能替代接口权限。
|
||
|
||
Cookie 登录/退出用 `dever.auth.issue_cookie(subject, session, tenant)` 与 `clear_cookie()`。Header/Cookie 使用 `dever.api` 读取/设置,无需显式 context 参数。密钥来自部署配置;Cookie 写入和 Cookie 认证的写请求需要精确同源校验,站点配置 origin。敏感头不能当普通 Text 读取。
|
||
|
||
## Model、CRUD 与业务编排
|
||
|
||
`module/content/article/model.dever` 的主记录是 Article,标题在 REST 写入时使用同领域纯计算规则:
|
||
|
||
源码:[文章 Model](../assets/examples/http-rest/module/content/article/model.dever)
|
||
|
||
```dever
|
||
type Article {
|
||
title: Text(1, 200) create = domain.normalize(input) replace = domain.normalize(input)
|
||
body: Text
|
||
}
|
||
```
|
||
|
||
自动有只读 `id` 与 `created_at`。字段支持 default、private、unique、index;范围和长度在存储边界验证。关系使用目标 Model 专有 id 字段,不用 Int 代替外键。
|
||
|
||
同领域 App 直接使用 `model.create({...})`、`model.get(id)`、`model.update(id,{...})`、`model.delete(id)` 和 `model.list({where=...,order=[created_at.desc,id.desc],page=1,size=20})`。
|
||
|
||
这些表达式由编译器检查,不是任意字典拼 SQL;无需再写 repository。关联查询和跨表插入由 App 明确编排,不会根据关系声明自动猜测级联写入顺序。
|
||
|
||
纯粹 CRUD 可在 API 写 `rest model`,多 Model 时指定 `rest model.<topic>`。Model 中 `create = expr`、`replace = expr`、`search = expr` 控制 REST 字段来源/查询;引用 input 表示客户端同名输入转换,不引用 input 表示服务端提供且禁止客户端提交。不要改成 post/put/get 字段关键字。
|
||
|
||
`private owner user_id: ... = dever.auth.user_id()` 创建时写入当前用户,并让全部读写 SQL 都带上所有权条件;它不只是隐藏输出字段。仅需这种隔离时,不用再写 App。
|
||
|
||
有关联操作、复杂输入输出、发布状态流转或超出字段约束的业务校验时,再写 App。API 中不放这些逻辑。App 用明确的 View 返回数据,避免泄漏 Model 私有字段。
|
||
|
||
不为每个 CRUD 机械建立 App、Domain、Port 和 Adapter;Model 的数据库操作无需再套一层 Port/repository。实际新增业务合同或编排时才增加对应实现。完整反重复、反硬编码及检查边界见 [项目开发](development.md)。
|
||
|
||
## 租户、任务与部署配置
|
||
|
||
配置只有 database 租户隔离:`tenant.database` 选控制连接,`global type` 使用平台库,其余持久 Model 与 Job 使用租户库。没有 field/table/mode 配置。租户缺失、未迁移或 schema 过期明确失败,不回退平台库。
|
||
|
||
运维命令:`dever tenant migrate <root> <id>`;`dever tenant owner <root> <id> <site> <user-id>` 建立核心 Owner;`dever tenant component enable|disable <root> <id> <component>` 控制租户功能。可用租户组件从 Model/Job 推导,不手写 Profile 分组。
|
||
|
||
job.dever 声明持久任务并绑定 App;enqueue 与事务、重试、调度、权限重验使用核心机制,不另写后台死循环。直接从随 skill 提供的 [Job 示例](examples.md#持久-job) 复用声明、配置和隔离测试,不能把第三方 Worker 当持久 Job。
|
||
|
||
部署配置先参考随 skill 提供的 [完整示例](examples.md),只添加项目用到的 database、tenant、auth、sites、http、job、runtime、log。最小 CMD 项目只需模板配置。密码、token 与真实数据库连接不提交 Git。
|