// facade 行 VO + 参数助手 —— 对齐 java *RowToMap / arg 助手(C2/C3/C15)
///|
fn obj_json(m : Map[String, Json]) -> Json {
@json.empty_object() |> ignore
let o = @json.empty_object()
for k, v in m {
@json.obj_set(o, k, v)
}
o
}
///|
pub fn define_row_to_json(r : @model.DefineRow) -> Json {
let o = @json.empty_object()
@json.obj_set(o, "id", i64_json(r.id))
@json.obj_set(o, "name", @json.string_of(r.name))
@json.obj_set(o, "display_name", @json.string_of(r.display_name))
@json.obj_set(o, "type", @json.string_of(r.define_type))
@json.obj_set(o, "state", num_json(r.state))
@json.obj_set(o, "version", num_json(r.version))
@json.obj_set(o, "create_time", opt_json(r.create_time))
@json.obj_set(o, "create_user", opt_json(r.create_user))
@json.obj_set(o, "update_time", opt_json(r.update_time))
@json.obj_set(o, "update_user", opt_json(r.update_user))
o
}
///|
pub fn task_row_to_json(r : @model.TaskRow) -> Json {
let instance_ext = parse_json_map(r.instance_variable)
let ext = parse_json_map(r.variable)
// issues/121 P1:引擎建单必写的控制键不算「任务变量非空」,否则新建任务的 ext 从此
// 永不再回退实例变量(82-3 既有契约)。
let control = match ext.get("isFirstTaskNode") {
Some(_) => 1
None => 0
}
if ext.length() - control == 0 {
for k, v in instance_ext {
ext[k] = v
}
}
let o = @json.empty_object()
@json.obj_set(o, "id", i64_json(r.id))
@json.obj_set(o, "process_instance_id", i64_json(r.process_instance_id))
@json.obj_set(o, "task_name", @json.string_of(r.task_name))
@json.obj_set(o, "display_name", @json.string_of(r.display_name))
@json.obj_set(o, "task_type", num_json(r.task_type))
@json.obj_set(o, "perform_type", num_json(r.perform_type))
@json.obj_set(o, "task_state", num_json(r.task_state))
@json.obj_set(o, "operator", opt_json(r.operator))
@json.obj_set(o, "finish_time", opt_json(r.finish_time))
@json.obj_set(o, "expire_time", opt_json(r.expire_time))
@json.obj_set(o, "form_key", opt_json(r.form_key))
@json.obj_set(o, "task_parent_id", opt_i64_json(r.task_parent_id))
@json.obj_set(o, "create_time", opt_json(r.create_time))
@json.obj_set(o, "create_user", opt_json(r.create_user))
@json.obj_set(o, "update_time", opt_json(r.update_time))
@json.obj_set(o, "update_user", opt_json(r.update_user))
@json.obj_set(o, "process_define_name", opt_json(r.define_name))
@json.obj_set(o, "process_define_display_name", opt_json(r.define_display_name))
@json.obj_set(o, "ext", obj_json(ext))
@json.obj_set(o, "instance_ext", obj_json(instance_ext))
@json.obj_set(o, "instance_create_time", opt_json(r.instance_create_time))
@json.obj_set(o, "version", opt_num_json(r.define_version))
@json.obj_set(o, "task_form_data", form_data_of_map(parse_json_map(r.variable), "tf_"))
o
}
///|
pub fn instance_row_to_json(r : @model.InstanceRow) -> Json {
let o = @json.empty_object()
@json.obj_set(o, "id", i64_json(r.id))
@json.obj_set(o, "parent_id", opt_i64_json(r.parent_id))
@json.obj_set(o, "process_define_id", i64_json(r.process_define_id))
@json.obj_set(o, "state", num_json(r.state))
@json.obj_set(o, "parent_node_name", opt_json(r.parent_node_name))
@json.obj_set(o, "business_no", opt_json(r.business_no))
@json.obj_set(o, "operator", @json.string_of(r.operator))
@json.obj_set(o, "expire_time", opt_json(r.expire_time))
@json.obj_set(o, "create_time", opt_json(r.create_time))
@json.obj_set(o, "create_user", opt_json(r.create_user))
@json.obj_set(o, "update_time", opt_json(r.update_time))
@json.obj_set(o, "update_user", opt_json(r.update_user))
@json.obj_set(o, "process_define_name", opt_json(r.define_name))
@json.obj_set(o, "process_define_display_name", opt_json(r.define_display_name))
@json.obj_set(o, "process_define_version", opt_num_json(r.define_version))
@json.obj_set(o, "ext", obj_json(parse_json_map(r.variable)))
@json.obj_set(o, "display_name", opt_json(r.define_display_name))
@json.obj_set(o, "version", opt_num_json(r.define_version))
o
}
///|
pub fn design_to_json(d : @model.ProcessDesign) -> Json {
let o = @json.empty_object()
@json.obj_set(o, "id", i64_json(d.id))
@json.obj_set(o, "name", @json.string_of(d.name))
@json.obj_set(o, "display_name", @json.string_of(d.display_name))
@json.obj_set(o, "type", @json.string_of(d.design_type))
@json.obj_set(o, "icon", opt_json(d.icon))
@json.obj_set(o, "is_deployed", num_json(d.is_deployed))
@json.obj_set(o, "remark", opt_json(d.remark))
@json.obj_set(o, "create_time", opt_json(d.create_time))
@json.obj_set(o, "create_user", opt_json(d.create_user))
@json.obj_set(o, "update_time", opt_json(d.update_time))
@json.obj_set(o, "update_user", opt_json(d.update_user))
o
}
///|
pub fn surrogate_to_json(s : @model.ProcessSurrogate) -> Json {
let o = @json.empty_object()
@json.obj_set(o, "id", i64_json(s.id))
@json.obj_set(o, "process_name", @json.string_of(s.process_name))
@json.obj_set(o, "operator", @json.string_of(s.operator))
@json.obj_set(o, "surrogate", @json.string_of(s.surrogate))
@json.obj_set(o, "start_time", opt_json(s.start_time))
@json.obj_set(o, "end_time", opt_json(s.end_time))
@json.obj_set(o, "enabled", num_json(s.enabled))
@json.obj_set(o, "create_time", opt_json(s.create_time))
@json.obj_set(o, "create_user", opt_json(s.create_user))
@json.obj_set(o, "update_time", opt_json(s.update_time))
@json.obj_set(o, "update_user", opt_json(s.update_user))
o
}
///|
/// 对齐 java taskVo
pub fn task_vo(t : @model.ProcessTask) -> Json {
let o = @json.empty_object()
@json.obj_set(o, "id", i64_json(t.task_id))
@json.obj_set(o, "process_instance_id", i64_json(t.process_instance_id))
@json.obj_set(o, "task_name", @json.string_of(t.task_name))
@json.obj_set(o, "display_name", @json.string_of(t.display_name))
@json.obj_set(o, "task_type", num_json(t.task_type))
@json.obj_set(o, "perform_type", num_json(t.perform_type))
@json.obj_set(o, "task_state", num_json(t.task_state))
@json.obj_set(o, "operator", opt_json(t.actor_id))
@json.obj_set(o, "form_key", opt_json(t.form_key))
@json.obj_set(o, "task_parent_id", opt_i64_json(t.parent_task_id))
let actor_ids = @json.empty_array()
for a in t.actor_ids {
@json.arr_push(actor_ids, @json.string_of(a))
}
@json.obj_set(o, "task_actor_id_list", actor_ids)
@json.obj_set(o, "task_form_data", form_data_of_flow(t.variables, "tf_"))
@json.obj_set(o, "finish_time", opt_json(t.finish_time))
@json.obj_set(o, "create_time", opt_json(t.create_time))
o
}
///|
pub fn design_his_to_json(h : @model.ProcessDesignHis) -> Json {
let o = @json.empty_object()
@json.obj_set(o, "id", i64_json(h.id))
@json.obj_set(o, "process_design_id", i64_json(h.process_design_id))
@json.obj_set(o, "content", @json.string_of(h.content))
@json.obj_set(o, "create_time", opt_json(h.create_time))
@json.obj_set(o, "create_user", opt_json(h.create_user))
o
}
// ── 参数助手(C2/C3 双收;C15 ids/id 双收空显式报错)──
///|
pub fn arg_str(args : Map[String, Json], key : String) -> String? {
match args.get(key) {
Some(Json::String(s)) => Some(s)
_ => None
}
}
///|
/// i64 双收:数字/字符串;存在但不可解析 → "非法id"
pub fn arg_i64(args : Map[String, Json], key : String) -> Int64? raise @error.JeeflowError {
match args.get(key) {
None => None
Some(v) =>
match @json.as_i64(v) {
Some(n) => Some(n)
None =>
match v {
Json::String(s) =>
// parse_int64 是 `-> Int64 raise`:解析失败 ⇒ 非法 id(文案逐字不变)
try {
Some(@string.parse_int64(s))
} catch {
_ => raise @error.Business("非法id: \{s}")
}
_ => raise @error.Business("非法id: \{@json.stringify(v)}")
}
}
}
}
///|
pub fn arg_str_or(args : Map[String, Json], key : String, default : String) -> String {
arg_str(args, key).unwrap_or(default)
}
///|
/// operator 归一化(issues/129 案 A · spec 06-facade §2.5):**空串与缺键同档**——
/// `{"operator":""}`(含全空白串)视同未传,一并回落本栈 demo 缺省 `user1`。
/// 缺这一层时空串会被下游当真实值比对 ⇒「我的列表」悄悄变空页(160 实测 moon 空串档 0 行)。
/// ⚠️ 刻意不改通用取值函数 `arg_str_or`:那是各 key 共用的,「空串本身就是合法值」的 key 不许被吞。
/// ⚠️ 非空白时返回**原串**(不 trim),与 java `JeeflowFacade.operatorArg` 逐字同形;
/// issues/114 的 `arg_required_str`(operator 硬必填、缺失即报错、返回 trim 后值)是另一档语义,别混用。
pub fn operator_arg(args : Map[String, Json]) -> String {
let s = arg_str(args, "operator").unwrap_or("")
if s.trim(chars=" \t\r\n").is_empty() {
"user1"
} else {
s
}
}
///|
pub fn arg_i64_or(args : Map[String, Json], key : String, default : Int64) -> Int64 raise @error.JeeflowError {
match arg_i64(args, key) {
Some(v) => v
None => default
}
}
///|
/// 首个存在的 i64(多候选键)
pub fn arg_id(args : Map[String, Json], keys : Array[String]) -> Int64? raise @error.JeeflowError {
for k in keys {
match arg_i64(args, k) {
Some(v) => return Some(v)
None => ()
}
}
None
}
///|
/// actorIds:JSON 数组 / 逗号串**两形同判据**(issues/142 B 批 · spec 06-facade §2.11
/// 「归属值写侧归一」,owner 2026-09-30 拍「八栈一起收:两形同判据+写侧兜底+trim+哨兵」)。
///
/// 改前本函数"一参两尺":串臂 trim+丢空,数组臂却只 `if !s.is_empty()` ⇒ 不 trim、不折叠,
/// 于是 `[" a "]` 与 `["a"]` 判成两个人、`[" "]` 原样进写侧落一行 actor_id=" ";而同文件紧邻的
/// `create_cc_instance` 还额外过 @model 归一,`task_surrogate`(addCandidate/surrogate 同体)却不过。
///
/// 现在形状适配复用 `@engine.parse_actors`(issues/141 G10 为抄送腿落地的那一枚,§2.11 逐字搬到
/// 任务侧):数组里的数字按 repr 原文收敛成字符串(>2^53 不取整,D-M5-3)、true/false 转串、
/// Json null 丢弃(本栈无 undefined 档,不许串化成 "null");判据复用 `@model.normalize_actors`
/// (逐元素 trim、空串/纯空白丢弃、同一次调用折叠)。反向哨兵:`"0"`、`"00"` 都是有效值且是两个人。
/// 空入参档由调用方沿用各自既有的"缺参数"错误信封(§2.11 要求③:不新造错误码与文案)。
pub fn arg_actor_ids(args : Map[String, Json]) -> Array[String] {
@engine.parse_actors(args.get("actorIds"))
}
///|
/// 批量主键:{ids} 优先 {id} 兜底;空显式报错(issues/95)
pub fn arg_ids(args : Map[String, Json]) -> Array[Int64] raise @error.JeeflowError {
let out : Array[Int64] = []
match args.get("ids") {
Some(Json::Array(arr)) =>
for v in arr {
match @json.as_i64(v) {
Some(n) => out.push(n)
None =>
match v {
Json::String(s) =>
// 同上(数组档)
try {
out.push(@string.parse_int64(s))
} catch {
_ => raise @error.Business("非法id: \{s}")
}
_ => raise @error.Business("非法id: \{@json.stringify(v)}")
}
}
}
_ => ()
}
if out.is_empty() {
match arg_i64(args, "id") {
Some(v) => out.push(v)
None => ()
}
}
if out.is_empty() {
raise @error.Business("id 缺失或非法")
}
out
}
///|
/// submitType 双收(数字/字符串)
pub fn arg_submit_type(args : Map[String, Json], key~ : String = "submitType") -> Int64? raise @error.JeeflowError {
arg_i64(args, key)
}
///|
/// content:字符串/对象/顶层 JSON 兼容(issues/27)
pub fn content_bytes(args : Map[String, Json]) -> String? {
match args.get("content") {
Some(Json::String(s)) => Some(s)
Some(Json::Object(_)) | Some(Json::Array(_)) => Some(@json.stringify(args.get("content").unwrap()))
Some(other) =>
match other {
Json::Null => None
_ => Some(@json.stringify(other))
}
None => {
let copy : Map[String, Json] = Map([])
for k, v in args {
if k == "processDesignId" || k == "processDefineId" || k == "operator" || k == "id" || k == "pageNum" || k == "pageSize" {
continue
}
copy[k] = v
}
if copy.length() == 0 {
None
} else {
Some(@json.stringify(obj_json(copy)))
}
}
}
}
///|
/// candidate 行键归一(issues/80)
pub fn candidate_row(actor_id : String, real_name : String, extra : Map[String, Json]?) -> Json {
let row = @json.empty_object()
@json.obj_set(row, "id", @json.string_of(actor_id))
@json.obj_set(row, "realName", @json.string_of(if real_name.is_empty() { actor_id } else { real_name }))
match extra {
Some(src) => {
match src.get("userId") {
Some(v) => @json.obj_set(row, "userId", v)
None =>
match src.get("user_id") {
Some(v) => @json.obj_set(row, "userId", v)
None => ()
}
}
match src.get("userName") {
Some(v) => @json.obj_set(row, "userName", v)
None =>
match src.get("user_name") {
Some(v) => @json.obj_set(row, "userName", v)
None => ()
}
}
match src.get("deptName") {
Some(v) => @json.obj_set(row, "deptName", v)
None =>
match src.get("dept_name") {
Some(v) => @json.obj_set(row, "deptName", v)
None => ()
}
}
}
None => ()
}
row
}
///|
/// args → FlowData(剔除 facade 控制键)
pub fn args_to_flow_data(args : Map[String, Json], exclude : Array[String]) -> @json.FlowData {
let fd = @json.FlowData::new()
for k, v in args {
let mut skip = false
for e in exclude {
if k == e {
skip = true
}
}
if !skip {
fd.insert(k, v)
}
}
fd
}