chore: 提交所有工作进度 — SaaS 后端增强、Admin UI、桌面端集成

包含大量 SaaS 平台改进、Admin 管理后台更新、桌面端集成完善、
文档同步、测试文件重构等内容。为 QA 测试准备干净工作树。
This commit is contained in:
iven
2026-03-29 10:46:26 +08:00
parent 9a5fad2b59
commit 5fdf96c3f5
268 changed files with 22011 additions and 3886 deletions

View File

@@ -0,0 +1,11 @@
[package]
name = "json-transform"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View File

@@ -0,0 +1,50 @@
---
name: JSON Transform
id: json-transform
version: "1.0"
mode: wasm
description: |
将 JSON 数据按指定规则进行转换。
支持:字段提取、重命名、过滤、排序等常用转换操作。
author: ZCLAW
tags:
- json
- transform
- data
triggers:
- json转换
- json transform
- 数据转换
capabilities:
- json-parse
- json-transform
---
# JSON Transform
读取 stdin 中的 JSON 输入,按转换规则处理后输出到 stdout。
## 输入格式
```json
{
"data": { ... },
"transforms": [
{ "type": "pick", "fields": ["name", "age"] },
{ "type": "rename", "from": "name", "to": "fullName" },
{ "type": "sort", "field": "age", "order": "desc" }
]
}
```
## 输出格式
转换后的 JSON 对象。
## 构建
```bash
cd skills/json-transform
cargo build --target wasm32-wasi --release
cp ../../../target/wasm32-wasi/release/json_transform.wasm main.wasm
```

View File

@@ -0,0 +1,154 @@
//! JSON Transform — WASM guest module for ZCLAW skill system.
//!
//! Reads JSON from stdin, applies transforms, writes result to stdout.
//! Target: `wasm32-wasi`
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::io::{self, Read, Write};
#[derive(Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
enum Transform {
Pick { fields: Vec<String> },
Rename { from: String, to: String },
Filter { field: String, op: String, value: Value },
Sort { field: String, order: String },
}
#[derive(Deserialize)]
struct Input {
data: Value,
#[serde(default)]
transforms: Vec<Transform>,
}
#[derive(Serialize)]
struct Output {
success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
impl Output {
fn ok(data: Value) -> Self {
Self {
success: true,
data: Some(data),
error: None,
}
}
fn err(msg: impl Into<String>) -> Self {
Self {
success: false,
data: None,
error: Some(msg.into()),
}
}
}
#[no_mangle]
pub extern "C" fn _start() {
let mut input_str = String::new();
if let Err(e) = io::stdin().read_to_string(&mut input_str) {
write_output(&Output::err(format!("Failed to read stdin: {}", e)));
return;
}
let input: Input = match serde_json::from_str(&input_str) {
Ok(v) => v,
Err(e) => {
write_output(&Output::err(format!("Invalid JSON input: {}", e)));
return;
}
};
let mut data = input.data;
for transform in input.transforms {
data = match apply_transform(data, &transform) {
Ok(v) => v,
Err(e) => {
write_output(&Output::err(e));
return;
}
}
}
write_output(&Output::ok(data));
}
fn apply_transform(data: Value, transform: &Transform) -> Result<Value, String> {
match transform {
Transform::Pick { fields } => {
let obj = data.as_object().ok_or("Pick requires object input")?;
let mut result = serde_json::Map::new();
for field in fields {
if let Some(v) = obj.get(field) {
result.insert(field.clone(), v.clone());
}
}
Ok(Value::Object(result))
}
Transform::Rename { from, to } => {
let mut obj = data.as_object().ok_or("Rename requires object input")?.clone();
if let Some(v) = obj.remove(from) {
obj.insert(to.clone(), v);
}
Ok(Value::Object(obj))
}
Transform::Filter { field, op, value } => {
let arr = data.as_array().ok_or("Filter requires array input")?;
let filtered: Vec<Value> = arr
.iter()
.filter(|item| {
let item_val = item.get(field);
match op.as_str() {
"eq" => item_val == Some(value),
"ne" => item_val != Some(value),
"gt" => compare_values(item_val, value).map(|c| c.is_gt()).unwrap_or(false),
"lt" => compare_values(item_val, value).map(|c| c.is_lt()).unwrap_or(false),
_ => false,
}
})
.cloned()
.collect();
Ok(Value::Array(filtered))
}
Transform::Sort { field, order } => {
let mut arr = data.as_array().ok_or("Sort requires array input")?.clone();
let field = field.clone();
let ascending = order != "desc";
arr.sort_by(|a, b| {
let va = a.get(&field).unwrap_or(&Value::Null);
let vb = b.get(&field).unwrap_or(&Value::Null);
let cmp = compare_values(Some(va), vb).unwrap_or(std::cmp::Ordering::Equal);
if ascending { cmp } else { cmp.reverse() }
});
Ok(Value::Array(arr))
}
}
}
fn compare_values(a: Option<&Value>, b: &Value) -> Option<std::cmp::Ordering> {
let a = a?;
match (a, b) {
(Value::Number(an), Value::Number(bn)) => {
if let (Some(af), Some(bf)) = (an.as_f64(), bn.as_f64()) {
Some(af.partial_cmp(&bf).unwrap_or(std::cmp::Ordering::Equal))
} else {
None
}
}
(Value::String(as_), Value::String(bs)) => Some(as_.cmp(bs)),
_ => None,
}
}
fn write_output(output: &Output) {
let json = serde_json::to_string(output).unwrap_or_else(|_| r#"{"success":false}"#.to_string());
let _ = io::stdout().write_all(json.as_bytes());
let _ = io::stdout().flush();
}