Appearance
jeeflow-moon 集成指南
面向 mldong 系框架/业务方:如何在宿主应用中嵌入 MoonBit 版 jeeflow 引擎(第 7 语言)。
安装(mooncakes.io)
在宿主模块用 moon add 按需引入(不带版本 = 拉 latest,自动写精确版本进 moon.mod; repository-mysql 的传递依赖 moondb/moonmysql/async 一并解析,无需手声明):
bash
moon add mldong/jeeflow-core # 必需:引擎核心(运行时零依赖)
moon add mldong/jeeflow-facade # 推荐:40+ action 统一门面
moon add mldong/jeeflow-persist # 可选:业务数据动态入库(ARCHIVE/SYNC)
moon add mldong/jeeflow-repository-mysql # 可选:MySQL 仓储(上游 moonmysql client,wasm 可直连)最小装配(内存仓储)
⚠️ 本节是装配形状图,不是可粘贴即编译的代码:
my_*/gen/args是占位符,包别名要写进 消费者自己的cmd/main/moon.pkg,且flow是async。逐字跑通的完整模块见 getting-started.md「5 分钟上手」。
moonbit
// 引擎为泛型 [R : ProcessRepository, E : ProcessExtRepository];小 SPI 走闭包字段
// 供数/回调族是 **async 闭包**(issues/146):本栈查库只有异步一条路,同步形状等于逼集成方启动期快照。
// 纯内存实现改走 *_sync 便捷口(内部经统一异步桥),注册完的注册表类型与异步通道一致。
let repo = @memory.MemoryRepository::new()
let ctx = @spi.Ctx::new(repo, repo) // 无扩展仓储时第二个参数用 NoExtRepository::new()
let ctx = ctx
.with_id_generator(fn() { gen.next_id() }) // IIdGenerator(未注入回退默认雪花;纯计算档保持同步)
.with_user_provider(async fn(uid : String) raise @error.JeeflowError {
// 现场查库:每次解析参与人都读最新数据,运行期新增/调岗免重启可见
@sys.user_by_id(uid)
})
.with_org_user_provider(my_org_fns) // IOrgUserProvider 三闭包组(同 async)
.with_user_search_provider(my_search_fns) // IUserSearchProvider(同 async)
.with_expression_evaluator(my_eval) // 可选;缺省内置简单比较求值
let facade = @facade.Facade::make(ctx)
// 40+ action 单入口(async;args 是 Map[String, Json]):
let resp = facade.flow("processDefine/startAndExecute", args) // {code,msg,data}MySQL 仓储
moonbit
let repo = @repo.MysqlRepository::from_env() // JEFFLOW_DB_HOST/PORT/USER/PWD/NAME
let facade = @facade.Facade::make(ctx_with(repo))- 每操作独立连接、语句级 autocommit(联邦现状,对齐 rust/sqlx 线)。
- 真事务:
let r = repo.new_request()→r.execute_in_tx(op)(op 收绑定后的仓储实例)—— op 内该实例族的仓储调用共用同一条连接,回调抛错整体回滚(spec/05 连接级 + 事务作用域绑定)。 句柄随仓储实例走、不是进程级全局槽:一个请求=一个仓储实例。 注册表跟着派生用ctx.for_request(请求实例, ext)——它是新建一份(容器逐个 copy), 不是原地改字段;本栈结构体是引用语义,原地改会让所有请求共用同一个事务槽。 ⚠ 外包facade.flow(...)时必须检查出口code并按需抛出:flow吞异常成信封 ⇒ 不抛就COMMIT, 半完成实例照旧落库(判据桩在demo/cmd/t1_mysql的 T1-F122)。 - 建表 DDL:
repository-mysql/schema/schema-mysql.sql(编辑源=jeeflow-java,勿手改)。
业务数据动态入库(persist)
moonbit
let provider = @persist.InMemoryMetaProvider::new()
provider.register(my_table_meta) // TableMeta:表/列/权限
let interceptor = @persist.PersistPostInterceptor::make(provider, my_writer)
ctx.register_interceptor(interceptor.as_interceptor()) // order=100 后置- 流程 JSON 顶层
"persistMode": "ARCHIVE" | "SYNC"+"relTableName": "业务表"。 - ARCHIVE:结束+FINISHED+同意 → 幂等 INSERT(键 process_instance_id)。
- SYNC:发起 INSERT → 任务 UPDATE(按目标节点
field.PERMISSION_*过滤)→ 结束定稿。
与 jeeflow-ui 直连
demo/cmd/main 即可运行的演示服务(:8092):
bash
JEEFLOW_DEMO_STORE=memory moon run --target wasm demo/cmd/main⚠️ 前缀拼法两套并存,别写成
JEFFLOW_DEMO_STORE:demo 侧是JEEFLOW_*(JEEFLOW_DEMO_STORE/JEEFLOW_TZ_OFFSET),MySQL 连接侧才是JEFFLOW_DB_*。App::create()只认逐字memory/mysql(大小写敏感),其它一律回落内存:拼错名、 或值写成Mysql/MYSQL/空串——v0.1.24 起会各打一行警告:…点名原因 (v0.1.23 及以前是静默回落,/api/reset照样回code=0,最容易骗过人)。 想确认存储模式,任何时候都只看启动横幅里的store=。
路由契约:POST /wf/{action}(全转发 facade)+ GET /health + POST /api/reset + GET /api/stats(无查询串,operator 缺省 user1——wasm 路由不剥 ? 后缀,带查询串会 unknown path)。
时钟基准(宿主注入)
本栈只有一把钟:core/model/clock.mbt 的 current_time_str()。写库审计列 (create_time/update_time/finish_time)、autoGenTitle、转办留痕 time、委托生效窗判据、 stats 的 todayNew/逾期数全部走这一个出口。
moonbit
// 宿主(框架壳 / demo / 测试)注入自己的本地钟;不注入 = 默认臂 UTC
@model.set_clock(Some(fn() { @model.format_unix_utc(@model.epoch_secs() + 28800L) }))
// 复原默认臂
@model.set_clock(None)为什么默认是 UTC:MoonBit 标准库只有 @env.now()(epoch 毫秒),没有时区包, 纯 std 拿不到本地偏移。 的裁定是「基准由宿主注入,引擎不自取」, 且明确不自行 @extern 造 FFI 偏门——所以未注入时宁可给 UTC,也不猜区。 契约句在 jeeflow-doc 的 spec/06-facade.md §2.4。
三条纪律(0.1.12 起有门禁钉住,见 MAINTAINING.md D-M6-2):
- SQL 侧不许出现
NOW()。repository-mysql六条写路径(instance/task/design/surrogate 的update_time,task_actor/cc_instance 的create_time)改绑引擎钟——NOW()取的是 数据库会话时区,与引擎那把钟不同基准(开发服务器 MySQL 实测+08:00,差 8 小时)。 - 判窗与写库同基准:委托生效窗与 stats 逾期都读
current_time_str(),不再各自取裸墙钟。 - 注入要能被测试确定化:
set_clock就是给用例铺固定时刻用的(moon test里注入、defer复原)。
demo 已内置环境变量注入,无需写代码:
bash
JEEFLOW_TZ_OFFSET=8 moon run --target wasm demo/cmd/main # 东八墙钟
moon run --target wasm demo/cmd/main # 不配 ⇒ UTC取值:东为正的小时偏移,支持 8 / +8 / -5 / 5.5 / +05:30,上限 UTC+14; 未配或非法 ⇒ 保持 UTC,并在启动行打印所选基准(时钟基准 = ...)。线上 demo (.github/workflows/demo-deploy.yml)已带 -e JEEFLOW_TZ_OFFSET=8。
环境变量
| 变量 | 说明 |
|---|---|
JEFFLOW_DB_HOST/PORT/USER/PWD/NAME | MySQL 连接(凭据不入仓) |
JEEFLOW_DEMO_STORE | demo 存储模式 memory(默认)/mysql |
JEEFLOW_TZ_OFFSET | demo 时钟基准偏移(东为正小时,如 8/+05:30;未配=UTC) |
LISTEN_ADDR | demo 监听地址(默认 0.0.0.0:8092) |
SKIP_MYSQL | 开发机跳过 T1(发版机连不上=fail) |