// MetaTableReader —— spec/10 §6 的元数据驱动读侧(java `persist/meta/MetaTableReader.java` 同位)。
//
// 与写侧共用同一份元数据 ⇒ storageType 语义两侧一致:EXPAND 反展开、JSON 反序列化、
// ONE2ONE/ONE2MANY 按外键=主表主键组装成对象/数组(子表若有自己的元数据则递归组装)。
// 无元数据 ⇒ 原样返回原始行(列名→值)。
//
// 数据来源是**端口**而不是引擎自己的 JDBC:本栈的读侧通路历来由宿主提供
// (`Ctx.biz_data_reader`,145/149 那几轮定的形状),这里给一对同签名闭包,
// 宿主用它包一层就能把 `as_biz_data_reader` 直接塞进 `with_biz_data_reader`
// ⇒ facade/core 的调用点与出口纪律一字不动(spec/12 §12.1「钉行为不钉机制」)。
///|
/// 一次「按列等值」的行查询请求(表 / 列 / 值)。
/// 三参数打平成**一个请求结构**,不是省字符:本栈的 `async` 闭包若体内不 await 就判
/// `unused_async`,而 `@spi` 的同步→异步桥只有 1/2 参两档(`async_of_sync` / `async_of_sync2`),
/// 单参数档才接得上 ⇒ 纯内存实现经桥注册、零新增告警(同 `InMemoryMetaProvider::as_provider` 那条)。
pub(all) struct TableKeyQuery {
table : String
column : String
value : Int64
}
///|
/// 行读取端口:按「列等值」取单行 / 多行。列名与值都是引擎内部给的(外键列来自元数据),
/// 实现方(宿主/仓储)自己负责防注入。
pub(all) struct DynamicTableReaderFns {
row_by_key : (async (TableKeyQuery) -> Map[String, Json]? raise @error.JeeflowError)
rows_by_key : (async (TableKeyQuery) -> Array[Map[String, Json]] raise @error.JeeflowError)
}
///|
pub(all) struct MetaTableReader {
meta : DynamicMetaProviderFns
reader : DynamicTableReaderFns
}
///|
pub fn MetaTableReader::make(
meta : DynamicMetaProviderFns,
reader : DynamicTableReaderFns
) -> MetaTableReader {
{ meta, reader }
}
///|
/// JSON 列 → 值(java `fromJson:130-136`:解不回就原样带出,容错不炸回显)。
fn from_json_column(v : Json) -> Json {
match v {
Json::String(s) =>
try {
@json.parse_json(s)
} catch {
_ => @json.string_of(s)
}
_ => v
}
}
///|
/// 该列是不是某个字段的 EXPAND 展开列(spec/10 §6「展开列不重复平铺」)
fn is_expand_column(meta : TableMeta, column : String) -> Bool {
let mut hit = false
for f in meta.fields {
for _, col in f.expand_fields {
if column_names_equal(col, column) {
hit = true
}
}
}
hit
}
///|
/// 按元数据组装回显结果(键=**表单字段名**;java `assemble:50-79` 同判据)
pub async fn MetaTableReader::assemble(
self : MetaTableReader,
meta : TableMeta,
row : Map[String, Json]
) -> Map[String, Json] raise @error.JeeflowError {
let result : Map[String, Json] = Map([])
for f in meta.fields {
let v = match find_data_key(row, f.column_name) {
Some(k) => Some(row[k])
None => None
}
match f.storage_type {
Json => result[f.name] = match v {
Some(pv) => from_json_column(pv)
None => @json.null_json()
}
Expand => {
let obj = @json.empty_object()
let mut any = false
for sub_name, col in f.expand_fields {
match find_data_key(row, col) {
Some(k) => {
@json.obj_set(obj, sub_name, row[k])
any = true
}
None => ()
}
}
if any {
result[f.name] = obj
}
}
One2One =>
match self.read_sub_table(meta, f, row) {
Some(sub) => result[f.name] = sub
None => ()
}
One2Many =>
match self.read_sub_table(meta, f, row) {
Some(sub) => result[f.name] = sub
None => ()
}
Normal =>
match v {
Some(pv) => result[f.name] = pv
None => ()
}
}
}
// 未在元数据里的列(process_instance_id / apply_user_id / 系统字段)原样带出,键统一小写
// (跨方言一致,java `:73-77`);EXPAND 展开列已消费 ⇒ 不再平铺
for k, kv in row {
if meta.find_field_by_column(k) is None && !is_expand_column(meta, k) {
let low = k.to_lower()
if result.get(low) is None {
result[low] = kv
}
}
}
result
}
///|
/// 子表读取(java `readSubTable:102-119`):外键=主表主键;子表有元数据就递归组装,
/// 没有就原样带出行。主键取不到(列缺失/非整数)⇒ None(不猜、不插空对象)。
async fn MetaTableReader::read_sub_table(
self : MetaTableReader,
parent_meta : TableMeta,
f : FieldMeta,
row : Map[String, Json]
) -> Json? raise @error.JeeflowError {
if f.target_table.is_empty() {
return None
}
let pk_key = match find_data_key(row, parent_meta.primary_key) {
Some(k) => k
None => return None
}
let parent_pk = match @json.as_i64(row[pk_key]) {
Some(n) => n
None => return None
}
let fk = if f.foreign_key.is_empty() { parent_meta.primary_key } else { f.foreign_key }
let sub_meta = (self.meta.load_table_meta)(f.target_table)
match f.storage_type {
One2One =>
match (self.reader.row_by_key)(TableKeyQuery::{
table: f.target_table, column: fk, value: parent_pk
}) {
Some(sub_row) =>
match sub_meta {
Some(m) => Some(@json.object_of(map_entries(self.assemble(m, sub_row))))
None => Some(@json.object_of(map_entries(sub_row)))
}
None => None
}
_ => {
let rows = (self.reader.rows_by_key)(TableKeyQuery::{
table: f.target_table, column: fk, value: parent_pk
})
let items : Array[Json] = []
for sub_row in rows {
match sub_meta {
Some(m) => items.push(@json.object_of(map_entries(self.assemble(m, sub_row))))
None => items.push(@json.object_of(map_entries(sub_row)))
}
}
Some(@json.array_of(items))
}
}
}
///|
/// Map → 有序键值对(`@json.object_of` 的入参形状)
fn map_entries(m : Map[String, Json]) -> Array[(String, Json)] {
let out : Array[(String, Json)] = []
for k, v in m {
out.push((k, v))
}
out
}
///|
/// 按流程实例回显一条业务数据(定位键=`process_instance_id`,与写入幂等键同款)。
/// 无记录 ⇒ None;无元数据 ⇒ 原始行(列名→值)。
pub async fn MetaTableReader::read_by_process_instance(
self : MetaTableReader,
table : String,
instance_id : Int64
) -> Map[String, Json]? raise @error.JeeflowError {
let row = match (self.reader.row_by_key)(TableKeyQuery::{
table, column: "process_instance_id", value: instance_id
}) {
Some(r) => r
None => return None
}
match (self.meta.load_table_meta)(table) {
None => Some(row) // 无元数据:原样返回
Some(meta) => Some(self.assemble(meta, row))
}
}
///|
/// 包成与 `Ctx.biz_data_reader` 同签名的闭包 ⇒ 宿主 `with_biz_data_reader(...)` 一行接好,
/// facade 的 bizData 出口与 issues/137 的泄漏纪律都不动。
pub fn MetaTableReader::as_biz_data_reader(
self : MetaTableReader
) -> (async (String, Int64) -> Map[String, Json]? raise @error.JeeflowError) {
let it = self
async fn(table : String, instance_id : Int64) -> Map[String, Json]? raise @error.JeeflowError {
it.read_by_process_instance(table, instance_id)
}
}