5.8 KiB
按需复用的完整项目
这些项目随 skill 提供,都包含 module/、test/ 和 config/setting.json。复制需要的项目到新目录,再替换业务内容,不要把整个 examples 放进 module。最小项目直接用 dever new 创建。
| 场景 | 项目资产 | 首次阅读 |
|---|---|---|
| HTTP、受保护 REST、真实身份核验 | http-rest | config/setting.json、module/access/session/app.dever、module/content/article/ |
| 入队、持久 Job、隔离数据库与虚拟时钟 | jobs | module/report/archive/、test/report/archive/queued.dever |
| 外部能力、声明失败、每例独立 fake | external-lib | 三语言接入说明 |
| 筛选、映射、累计与逐项断言 | collections | 集合与处理函数 |
| 普通二进制 Port、CMD 与 fake | command | 二进制调用说明 |
复制后先运行 dever fmt <root> --check 和 dever check <root>,再按下面的命令运行相关测试。这些检查不准备 Worker、不连接部署数据库、不启动 HTTP/Job 服务。
文件要放在示例对应的角色中。遇到无法从业务入口调用到的代码报错(C016),接通真实调用或删除废弃代码,不添加假调用。
HTTP 与 REST
http-rest 展示的是受保护内容资源和已有登录系统的 verify 接口,不包含账号注册、密码登录或签发凭据。匿名健康检查 GET /system/health/admin/status/ready 返回 {"code":0,"message":"ok","data":true};它没有持久数据或租户依赖。
module/content/article/api/admin/entries.dever 的 rest model 生成 /content/article/admin/entries 下的资源接口;用 dever api <root> --output <new-file> 查看精确方法、路径和输入合同。REST 的权限键为 content.article.admin.read/create/replace/delete。例如创建正文包含 title、body,title 经同领域 pure Domain trim 后再应用 Model 长度约束。Model 自动提供 id/created_at,不手写 CRUD App。
配置中的 admin.path = "admin" 对应两个领域的 api/admin/**。
身份核验先读取 global Session,检查 UUID、subject、site、tenant key、过期与吊销状态,再检查用户、租户和成员关系。成功返回只有 id/user_id/tenant_id 的 Identity;失败返回 Unauthorized。不能原样回显 claims 冒充核验,也不从 verify 返回权限列表。
接入已有登录系统时,由它的认证流程创建 User、Tenant、Membership、Session,再按 provider 配置签发 subject/session/tenant。示例不会自动建默认用户、发演示 token 或绕过权限。
部署前替换 JWT 密钥,迁移租户数据库,再用 dever tenant owner <root> <positive-tenant-id> admin <positive-user-id> 建立初始 Owner。参数是数据库 ID,不是 token 的 tenant key。后续角色权限用核心 dever.auth 管理,不复制一套 RBAC 表。建 Owner 和迁移属于运维操作,不是普通测试。
这里开启 database 租户隔离:global 身份表在控制库,Article 在租户库,匿名 health 不触达 Article。不要添加 tenant.mode/field/table 或在缺少租户时回退控制库。站点没有使用 Cookie;如果改为 Cookie 登录,补充精确 sites.admin.origin 和 issue_cookie/clear_cookie 的同源合同。
定向测试:dever test <root> --filter content.article.title --timeout-ms 5000。该例验证标题规则,不证明 REST HTTP、JWT、权限或实际租户迁移已验收。
持久 Job
jobs 的 report.archive.submit CMD 接受一个 input record:
dever run <root> -- report.archive.submit '{"input":{"key":"report-2026-10","title":"Monthly report"}}'
这是会写示例数据库的显式运行命令,不是普通验证。CMD 只入队并退出;没有命令参数的服务启动才按配置 runtime.mode = "worker" 执行持久队列,不能为了跑测试启动它。
Job 声明 database default、同领域 App 参数类型、retry(3) 和 timeout(30000),执行时只调用 App。
入队和入口写入在同一事务中;key 用来识别同一请求,重复提交不会创建另一个任务。Job 执行有自己的事务,Model 的 request_key 唯一约束防止重复档案。失败由核心队列重试,不另写循环、sleep 或第三方 Worker。
dever test <root> --filter report.archive.queued --timeout-ms 5000 使用运行器拥有的临时 SQLite。测试重复入队验证同一任务 ID,通过 dever.test.drain_jobs(5) 执行已到期任务,使用 advance_clock(1000) 推进虚拟时钟。既不使用配置里的 data/jobs.db,也不启动真实后台服务。
CMD/Test 的可信执行身份由核心建立为 System。若改为 HTTP 入队,只能从受保护写 API 到达;public 不得入队。核心在每次 User Job 尝试时重新验证会话、租户、组件与入队接口的精确权限,不保存角色快照。
来源与验证边界
集合项目可直接运行 dever run <root> -- content.article.summarize '{"articles":[{"title":" First ","published":true},{"title":"Hidden","published":false}]}',结果包含 titles: ["First"] 和 title_length: 5。dever test <root> --filter content.article --timeout-ms 5000 同时运行集合业务和语法分句断言,无需数据库或服务。
这些项目保留了当前语言的角色边界、身份核验、Job 和 Port fake 用法,关键源码随 skill 提供。教程中的 dever 代码块必须有“源码”链接,内容与链接文件的连续片段逐字匹配,所属完整项目参加源码、业务入口、格式和测试检查。真实 Lib、数据库部署和 HTTP 仍需各自验收。